总资料
全是随笔 笔记 与 学习资料。没有规律。
常见多线程方法
dispatch_queue_global_t qH = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
并发的dispatch_queue_t que = dispatch_queue_create("jimboqueue", DISPATCH_QUEUE_CONCURRENT /*DISPATCH_QUEUE_SERIAL*/);
并发和串行自由控制- 因为性能原因,推介使用
dispatch_semaphore
和pthread_mutex
- 自旋锁,会在等待中持续占用
cpu
资源。忙等, 如:OSSpinLock
- 互斥锁,在等待中不会占用
cpu
资源,真正的在睡眠中。 - 自旋锁:atomic、OSSpinLock、dispatch_semaphore_t
- 互斥锁:pthread_mutex、@ synchronized、NSLock、NSConditionLock 、NSCondition、NSRecursiveLock
线程阻塞
- 在
当前
串行队列里面 添加sync
当前队列的任务 会造成线程死锁
。 - 在主线程同理,因为主线程就是串行队列
- 在当前串行队列任务未执行完成时,在本队列插入同步任务导致死锁
// dispatch_queue_t que = dispatch_queue_create("jimboqueue", DISPATCH_QUEUE_CONCURRENT); //不会死锁
dispatch_queue_t que = dispatch_queue_create("jimboqueue", DISPATCH_QUEUE_SERIAL); //死锁
dispatch_async(que, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(que, ^{
NSLog(@"2 %@", [NSThread currentThread]);
});
NSLog(@"3 %@", [NSThread currentThread]);
});
perforSelector:withObject:afterDelay
- (void)test{
NSLog(@"tset %s", __func__);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_queue_global_t q = dispatch_get_global_queue(0, 0);
dispatch_async(q, ^{
NSLog(@"1 %@", [NSThread currentThread]);
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3 %@", [NSThread currentThread]);
});
}
//2021-12-21 20:58:07.509883+0800 140_RunLoop_iOS[27151:1282963] 1 <NSThread: 0x600000b19b80>{number = 4, name = (null)}
//2021-12-21 20:58:07.510573+0800 140_RunLoop_iOS[27151:1282963] 3 <NSThread: 0x600000b19b80>{number = 4, name = (null)}
这个方法不会调用-test 方法.
- 因为
performSelector: withObject: afterDelay:
是RunLoop的文件中的,不开源的,通过调用堆栈可以看出。的底层堆栈含有__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
方法,是属于RunLoop
的范围,所以在当前线程必须存在RunLoop
才会进行调用。但是GCD创建的线程默认是不带RunLoop的。 - 其他不带
afterDelay
的方法如:performSelector: withObject:
在objc开源代码里面可以看出是直接调用的的objcmsgSend()
, 所以不会受影响。 - NSTimer 的所有方法都是runloop的,知识区别在于有没有捕获target
解决方法:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_queue_global_t q = dispatch_get_global_queue(0, 0);
dispatch_async(q, ^{
NSLog(@"1 %@", [NSThread currentThread]);
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3 %@", [NSThread currentThread]);
//runLoop 添加任务和启动
//[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode]; 可以不用添加任务,因为afterDelay dtimer已经被加入到任务中
[[NSRunLoop currentRunLoop] run];
});
}
备注:afterDelay如果不是RunLoop中的话,怎么保证timer几秒后触发的时候,线程不会被销毁呢,只有用RunLoop来保活线程。
各个类型的代码
- 递归锁只能在同一线程内递归使用,
- 不同线程内会进行枷锁
os_spin_lock
自旋锁会存在优先级反转,已经不支持了,废弃了
os_unfair_lock
#import <os/lock.h>
//声明
@property (assign, nonatomic) os_unfair_lock fairLock;
//初始化
self.fairLock = OS_UNFAIR_LOCK_INIT;
{
os_unfair_lock_lock(&_fairLock);
//执行代码
os_unfair_lock_unlock(&_fairLock);
}
Pthread_mutex_t
/**
#define PTHREAD_MUTEX_NORMAL 0 //普通锁. 不能递归,不然会死锁
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2 //递归所
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
*/
//初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
//初始化锁
pthread_mutex_init(&_mutex, &attr);
//销毁 attr
pthread_mutexattr_destroy (&attr);
{
pthread_mutex_lock(&_mutex);
pthread_mutex_unlock(&_mutex);
}
//销毁锁
pthread_mutex_destroy(&_mutex);
pthread condition 条件锁
//声明
@property (nonatomic, assign) pthread_mutex_t mutex;
@property (nonatomic, assign) pthread_cond_t cond;
------
//初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
//初始化锁
pthread_mutex_init(&_mutex, &attr);
pthread_mutexattr_destroy (&attr);
//初始化条件
pthread_cond_init(&_cond, NULL);
{
pthread_mutex_lock(&_mutex);
//等待进入
pthread_cond_wait(&_cond, &_mutex);
//激活一个等待线程
pthread_cond_signal(&_cond);
//激活所有等待的线程
pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}
//销毁锁
pthread_mutex_destroy(&_mutex);
//销毁条件
pthread_cond_destroy(&_cond);
NSLock
是对pthread_mutex
的PTHREAD_MUTEX_NORMAL
封装
@property (nonatomic, strong) NSLock *lo;
---
self.lo = [[NSLock alloc] init];
[self.lo lock];
//加锁代码
[self.lo unlock];
NSRecursiveLock
NSRecursiveLock
是对pthread_mutex
的PTHREAD_MUTEX_RECURSIVE
封装
NSCondition 和 NSConditionLock
NSCondition
是对pthread_mutex
和pthread_cond_t
的 封装NSConditionLock
是对pthread_mutex
和pthread_cond_t
的 封装NSConditionLock
会调用NSCondition
对象
dispatch_queue_t + DISPATCH_QUEUE_SERIAL
串行队列保证线程安全
dispatch_semaphore_t
信号量控制最大并发数量
dispatch_semaphore_create(int)
@synchronized
- @synchronized 是对mutex递归锁的封装
- 性能最差
// Begin synchronizing on 'obj'.
// Allocates recursive mutex associated with 'obj' if needed.
// Returns OBJC_SYNC_SUCCESS once lock is acquired.
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
ASSERT(data);
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
BOOL objc_sync_try_enter(id obj)
{
BOOL result = YES;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
ASSERT(data);
result = data->mutex.tryLock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
自旋锁、互斥锁
虽然自旋锁已经被iOS10被废弃
什么时候使用自旋锁比较划算
- 预计线程等待锁的时间比较短(这样线程可以不用去睡眠)
- 加锁的代码(临界区)经常被调用,多条线程同时竞争比较少的。
- CPU资源不紧张(多核CPU)
互斥锁什么时候划算
- 预计线程等待锁的时间比较长
- 单核处理器
- 临界区有IO操作
- 代码复杂,循环量比较大,执行时间长
- 临界区竞争非常激烈
读写锁
需要满足以下要求
- 同时只有一个线程写
- 可以有多个线程读
- 读和写同时只有一个
pthread_rwlock_t
@property(nonatomic, assign) pthread_rwlock_t rwLock;
----
pthread_rwlock_init(&_rwLock, NULL);
- (void)read {
pthread_rwlock_rdlock(&_rwLock); //读加锁
NSLog(@"%s", __FUNCTION__);
pthread_rwlock_unlock(&_rwLock); //通用解锁
}
- (void)write {
pthread_rwlock_wrlock(&_rwLock); //写加锁
NSLog(@"%s", __FUNCTION__);
pthread_rwlock_unlock(&_rwLock); //通用解锁
}
- (void)dealloc {
pthread_rwlock_destroy(&_rwLock); //结束后调用
}
dispatch_barrier_async()
//必须自定义队列
dispatch_queue_t queue = dispatch_queue_create("jimbo_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 20; i++) {
dispatch_async(queue, ^{
//read
NSLog(@"read");
sleep(1);
});
dispatch_barrier_async(queue, ^{
NSLog(@"writer");
sleep(1);
});
dispatch_async(queue, ^{
//read
NSLog(@"read");
sleep(1);
});
}
Atomic、nonatomic
可参考objc -> objc-accessors.mm文件内
//get方法
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
//atomic 添加自旋锁
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
//set方法 的最终调用
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
.......
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
//atomic 添加自旋锁
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
- atomic 用于保证属性setter、getter的原子性操作,相当于在其内部线程添加了线程同步的锁
- 但是它不能保证使用属性的过程是线程安全的. (因为只在方法内部加锁了)
@property(atomic, strong) NSMutableArray *mArr;
---
MYPerson *p = [[MYPerson alloc] init];
p.mArr = @[].mutableCopy; //set 方法线程安全
/**
这段代码不安全
p.mArr 这个get是安全的
addObject 是不安全的,因为是在get方法之外了
*/
[p.mArr addObject:@"1"];
备注:部分笔记包含有MJ老师的学习资料。