盘点iOS开发中的线程锁

线程锁使用场景:在多个线程下操作同一个数据,数据将变得不安全。比方说:在多个线程中删除一个数组的首个元素,你不知道在多线程操作过程中,该元素还存不存在,如果不存在程序就会崩溃。
加了线程锁以后,就能保证在A线程访问数据的时候,B线程就没有办法访问。只有在A线程执行完解锁操作以后,B线程才有资格去访问。也就是说该数据只允许被一个线程访问,这就是线程安全。

针对这个问题,我们一起来盘点下iOS开发中的线程锁。
一、 原子锁: atomic

atomic@property创建属性默认的关键字,使用atmoic关键字会在属性的setter方法里面加上了,如下面代码。因为手机设备资源有限,为了提高效率在iOS开发中我们一般上使用nonatomic关键字。

  {lock}
  if (property != newValue) {
     [property release]; 
     property = [newValue retain]; 
  }    
  {unlock}

Objective-C Property Attributes这篇文章中详细解释了atomicnonatomic。划重点:Atomic is really commonly confused with being thread-safe, and that is not correct. You need to guarantee your thread safety other ways.就是说用了atomic关键字并不能保证数据是线程安全的,它只能保证你拿到的值是完好无损的。

- (void)multiOperation {
    for (int i=0; i<10; i++) {
        NSString *queue = [NSString stringWithFormat:@"queue-%d", i];
        dispatch_queue_t q = dispatch_queue_create([queue UTF8String], NULL);
        dispatch_async(q, ^{
            self.string = queue;
        });
    }
}
调用多次的结果:
string === queue-1 
string === queue-5 
string === queue-4 
string === (null) 
string === (null) 
string === queue-0 
string === queue-8 
string === queue-6 

上面这个例子里面string属性是nonatomic修饰的,连续多次调用结果是随机的,中间也会出现string === (null)的情况。如果用atomic来修饰结果也是随机的但中间不会出现string === (null),这也就解释了上面的结论:atomic关键字并不能保证数据是线程安全的,它只能保证你拿到的值是完好无损的。
为什么会出现string === (null)呢?回到setter代码块,如果不加原子锁该属性在多线程赋值的过程中碰巧两个线程接连执行了release操作,当该属性的retainCount=0的时候也就释放了,所以就出现了null值。

二、NSLock & NSCondition & NSConditionLock & NSRecursiveLock

这四个是苹果封装好的线程锁对象,统一定义在NSFoundation -> NSLock.h文件里面,都遵守了NSLocking协议。

  @protocol NSLocking
  - (void)lock; 
  - (void)unlock;
  @end

所以,基本使用也很类似:

  1. 初始化一个锁对象
  2. 执行上锁操作[xxx lock]
  3. 执行解锁操作[xxx unlock]

不同的是使用场景:
NSLock:最简单的线程锁,没有复杂的需求使用它就好了。
NSCondition & NSConditionLock:条件锁,满足一定条件触发的线程锁。
NSRecursiveLock:递归锁,在递归调用使用线程锁很容易造成死锁,递归锁就是为了解决这些问题设计的。具体使用参照:NSRecursiveLock递归锁的使用

三、synchronized关键字

为了避免多个线程同时执行同一段代码,Objective-C提供了@synchronized()。它可以对一段代码进行加锁,同一时间只允许一个线程执行该代码,其他试图执行该代码的线程都会被阻塞。和NSLock等线程锁对比,@synchronized()使用起来更加方便,可读性更高。
至于@synchronized()的底层实现,可以看这篇文章关于 @synchronized,这儿比你想知道的还要多

四、信号量 dispatch_semaphore_t

信号量(dispatch_semaphore):信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。
在GCD中有三个函数是semaphore的操作,分别是:dispatch_semaphore_create:创建一个semaphore,需要传入一个long类型的数,作为信号总量。
dispatch_semaphore_signal:发送一个信号,让信号总量加1。
dispatch_semaphore_wait:发送一个等待信号,让信号总量减1

    dispatch_group_t group = dispatch_group_create();   
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);   
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   
    for (int i = 0; i < 100; i++)   
    {   
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);   
        dispatch_group_async(group, queue, ^{   
            NSLog(@"%i",i);   
            sleep(2);   
            dispatch_semaphore_signal(semaphore);   
        });   
    }   
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);   
    dispatch_release(group);   
    dispatch_release(semaphore);

这段代码创建了一个初始值为10的信号量,每一次循环都会发送一个等待信号,并创建一个线程,该线程执行完以后发送一个信号。当创建了10个线程以后,for循环就会阻塞,等待有线程执行完以后才会继续执行。这就形成了对并发的控制,上面是创建了一个并发数为10的线程队列。如果要做一个并发数为1的线程锁,只需要创建一个初始值为1的信号量就可以了。

五、补充 POSIX(pthread_mutex) & OSSpinLock

POSIX(pthread_mutex):Linux 线程锁详解
OSSpinLock:不再安全的 OSSpinLock
PS:POSIX(pthread_mutex)Linux底层的API,复杂的多线程处理建议使用,并且可以封装自己的多线程;OSSpinLock已经出现了BUG,导致并不能完全保证是线程安全的。


DEMO:
Objective-C-ThreadLock
参考文章:
iOS多线程 -各种线程锁的简单介绍
深入理解 iOS 开发中的锁

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容