iOS中的线程同步方案

前提简述:

常用的线程方案有Pthread,NSThread, GCD,NSOperation。以下是比较:
pthread : 是一套通用的C语言多线程API,适用于Unix \ Linux\ Windows等系统,可跨平台\可移植性,学习难度大。线程生命周期需要程序员管理。

NSThread: 是OC语言,使用更加面向对象,直接操作线程对象,程序员需要管理生命周期。

GCD: 可替代NSThread等线程技术,C语言实现,系统自动管理。

NSOperation: 基于GCD的封装成OC对象,比GCD多了一些更简单实用的功能,使用更加面向对象,系统自动管理线程生命周期

另附上两个案例:

 dispatch_async(dispatch_get_global_queue(0, 0), ^{

        NSLog(@"1");

        [self performSelector:@selector(test) withObject:nil afterDelay:0];

        NSLog(@"3");

    })

-(void)test { NSLog(@"2") ;}

打印结果是:1、3。

原因:performSelector…afterDelay是向runloop中添加了一个timer定时器,子线程中默认是没有启动runloop的,所以不会执行。

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event {

    NSThread*thread = [[NSThreadalloc]initWithBlock:^{

        NSLog(@"1");

    }];

    [threadstart];

    BOOLisOk =true;

    // isOk 设置为YES时,会导致crash, 因为在子线程runloop未开启,block执行完毕后,Threadj即退出;会提示此信息:performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform'

    // 设置为NO,test不执行。

    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:isOk];

}

多线程存在安全隐患,同一个数据可能会被多个线程共享,也就是多个线程可能会访问同一块资源,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。这种情况,我们需要使用线程同步技术(同步,就是协同步调,按预定的先后次序进行)解决。常用的方式是加锁。

iOS线程同步方案:

1, OSSpinLock 自旋锁:

OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源

目前已经不再安全,可能会出现优先级反转问题

如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁

需要导入头文件#import<libkern/OSAtomic.h>

OSSpinLock lock = OS_SPINLOCK_INIT;

OSSpinLockLock(&_moneyLock);

//coding here

 OSSpinLockUnlock(&_moneyLock);

2, os_unfair_lock

nos_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持

n从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等

n需要导入头文件#import<os/lock.h>

os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;

os_unfair_lock_lock(&_ticketLock);

// coding here

os_unfair_lock_unlock(&_ticketLock);

3, pthread_mutex

mutex叫做”互斥锁”,等待锁的线程会处于休眠状态。需要导入头文件#import<pthread.h>

{    // 静态初始化

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    // 初始化属性

    pthread_mutexattr_t attr;

    pthread_mutexattr_init(&attr);

    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);

//    初始化锁

    pthread_mutex_init(mutex, &attr);

//   销毁属性

    pthread_mutexattr_destroy(&attr);

    // 初始化锁  NULL表示默认属性

    pthread_mutex_init(mutex, NULL);

}

{

     pthread_mutex_lock(&_ticketMutex);

    //coding here...

    pthread_mutex_unlock(&_ticketMutex);

}

- (void)dealloc{

    pthread_mutex_destroy(&mutex);

}

4, pthread_mutex 递归锁

用法和3类似,上述pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)

5, pthread_mutex – 条件锁

使用方法展示:

 pthread_mutex_t mutex;

 pthread_mutex_init(&mutex, NULL); //NULL表示使用默认属性

  // 初始化条件

 pthread_cond_t condition;

 pthread_cond_init(&condition, NULL);

// 等待条件(进入休眠,释放mutex锁; 被唤醒后,会再次对mutex加锁)

pthread_cond_wait(&condition, &mutex);

//激活一个等待该条件的线程

pthread_cond_signal(&condition);

//激活所有等待该条件的线程

pthread_cond_broadcast(&condition);

- (void) del{

    pthread_mutex_lock(&_mutex);

    pthread_cond_wait(&_cond, &_mutex);

    // coding here

    pthread_mutex_unlock(&_mutex);

}

-(void)append {

    pthread_mutex_lock(&_mutex);

    // coding here

    pthread_cond_signal(&_cond);  // 信号

    pthread_mutex_unlock(&_mutex);

}

- (void)dealloc {

    pthread_mutex_destory(&mutex);

    pthread_cond_destory(&condition);
}

6,NSLock

NSLock是对mutex普通锁的封装, 遵守NSLocking协议

7,NSRecursiveLock

NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致

8,NSCondition

NSCondition是对mutex和cond的封装,使用演示如下:

NSCondition *condition = [[NSCondition alloc] init];

- (void)del{

    [condition lock];

    [condition wait];  // 等待

    // coding here

    [condition unlock];

}

- (void)add{

    [self.condition lock];

    // coding here

    [self.condition broadcast];

    [self.condition unlock];

}

9, NSConditionLock

NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值。简列使用如下:

int start = 0; 设置某个启动条件

NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition: start];

[conditionLock lock];

[conditionLock lockWhenCondition: some];

[conditionLock unlockWithCondition:some];

[conditionLock unlock];

10,dispatch_semaphore

semaphore叫做”信号量”,信号量的初始值,可以用来控制线程并发访问的最大数量,信号量的初始值为1,代表同时只允许1条线程访问资源,可以用来保证线程同步。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(x);

//当信号量的值<=0,当前线程就会进入休眠等待(直到信号量的值大于0)

//如果信号量的值>0,就减1,然后执行后面的代码

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

dispatch_semaphore_signal(semaphore);

11, dispatch_queue

直接使用GCD的串行队列,也是可以实现线程同步的。

12,@synchronized

@synchronized是对mutex递归锁的封装;@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作。

@synchronized (token) {}

13, atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁, 它并不能保证使用属性的过程是线程安全的。

线程同步方案性能比较,性能从高到低排序依次是:

os_unfair_lock > OSSpinLock > dispatch_semaphore > pthread_mutex > pdispatch_queue(DISPATCH_QUEUE_SERIAL) >  NSLock > NSCondition > ppthread_mutex(recursive) > NSRecursiveLock > NSConditionLock > @synchronized

自旋锁、互斥锁比较:

a, 什么情况使用自旋锁比较划算?

预计线程等待锁的时间很短

加锁的代码(临界区)经常被调用,但竞争情况很少发生

CPU资源不紧张

多核处理器

b,什么情况使用互斥锁比较划算?

预计线程等待锁的时间较长

单核处理器

临界区有IO操作

临界区代码复杂或者循环量大

临界区竞争非常激烈

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

推荐阅读更多精彩内容

  • 锁是一种同步机制,用于多线程环境中对资源访问的限制iOS中常见锁的性能对比图(摘自:ibireme): iOS锁的...
    LiLS阅读 1,455评论 0 6
  • 线程同步方案 OSSpinLock 自旋锁 os_unfair_lock pthread_mutex dispat...
    那位小姐阅读 1,630评论 0 19
  • 一、简介:多线程在之前进行过一篇详细的基础博客 iOS多线程 二、多线程的基础知识回顾 1.1、iOS中的常见多线...
    IIronMan阅读 833评论 0 4
  • Q:为什么出现多线程? A:为了实现同时干多件事的需求(并发),同时进行着下载和页面UI刷新。对于处理器,为每个线...
    幸福相依阅读 1,537评论 0 2
  • 转载自:https://www.jianshu.com/p/938d68ed832c# 一、前言 前段时间看了几个...
    cafei阅读 4,500评论 1 12