iOS多线程(四)

多线程安全隐患解决方案

1.解决方案:使用线程同步技术(协同步调,按预定的先后次序进行)

2.常用的线程同步技术:加锁

3.iOS中的线程同步方案

OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

3.1OSSpinLock

3.1.1OSSpinLock叫做自旋锁,等待锁的线程会处于忙等(busy-wait)状态,一直占用CPU资源
3.1.2 目前已经不安全,可能会出现优先级反转问题
3.1.3 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法 释放锁
3.1.4 头文件 #import <libkern/OSAtomic.h>
3.1.5 示例代码如下
- (void)p_saleTicket {
//初始化
    static OSSpinLock ossPinLock = OS_SPINLOCK_INIT;
// 尝试加锁(如果需要等待就不加锁,直接返回false,如果不需要等待就加锁返回true)
//bool result  = OSSPinLockTry(&ossPinLock);
//加锁
    OSSpinLockLock(&ossPinLock);
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
//解锁
    OSSpinLockUnlock(&ossPinLock);
}

3.2 os_unfair_lock

3.2.1 os_unfair_lock用于取代不安全的OSSPinLock,iOS10开始支持
3.2.2从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
3.2.3头文件#import<os/lock.h>
3.2.4示例代码如下
- (void)p_saleTicket3 {
    //初始化
    static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
    //尝试加锁
    //os_unfair_lock_trylock(&lock);
    //加锁
    os_unfair_lock_lock(&lock);
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
    //解锁
    os_unfair_lock_unlock(&lock);
}

3.3 pthread_mutex_t

3.2.1 mutex叫做"互斥锁 ",等待锁的线程会处于休眠状态
3.2.2头文件#import <pthread/pthread.h>
3.2.3示例代码如下
// 静态初始化
  //        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);
  
  // 初始化属性
//    pthread_mutexattr_t attr;
//    pthread_mutexattr_init(&attr);
//    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
  // 初始化锁
  pthread_mutex_init(mutex, NULL);
  // 销毁属性
//    pthread_mutexattr_destroy(&attr);

3.4 NSLock | NSRecursiveLock

  3.4.1.NSLock是对mutex普通锁的封装
  3.4.2.NSRecursiveLock也是对mutex递归锁的封装
 3.4.3示例代码如下
- (void)p_saleTicket {
   [self.lock lock];
   sleep(1);
   int oldCount = self.ticketsCount;
   oldCount--;
   self.ticketsCount = oldCount;
   NSLog(@"余票%d",oldCount);
   [self.lock unlock];
}

3.5 NSCondition

3.5.1NSCondition是对mutex和cond的封装
3.5.2常见API
- (void)wait; 让当前线程处于等待状态
- (BOOL)waitUntilDate:(NSDate *)limit;让当前线程在一定时间内处于等待状态
- (void)signal;CPU发信号告诉当前线程不用在等待,可以继续执行
- (void)broadcast;CPU发信号告诉所有线程不用在等待,可以继续执行
3.5.3示例代码如下
- (void)p_saleTicket {

    [self.lock lock];
    if (self.ticketsCount == 12) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 信号
            [self.lock signal];
        });
        // 等待
        [self.lock wait];
    }
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
    [self.lock unlock];

}

3.6 NSConditionLock

3.6.1示例代码如下
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one
{
    [self.conditionLock lock];
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two
{
    [self.conditionLock lockWhenCondition:2];
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three
{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

3.7 DISPATCH_QUEUE_SERIAL

3.7.4示例代码如下 放到串行队列里面

3.8 dispatch_semaphore_t

3.8.1semaphore叫做信号量
3.8.2信号量的初始值,可以用来控制线程并发访问的最大数量 不可以未负数
3.8.3信号量的初始值为1 ,代表同时只允许1条线程访问资源,保证线程同步
3.8.4示例代码如下
//    初始化信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//    如果信号量=0当前线程就会进入休眠(直到信号量的值>0)
//    如果信号量>0 就减1 然后继续往下执行后面的代码
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//    让信号量的值加1
    dispatch_semaphore_signal(semaphore);

3.8 @synchronized

  3.8.1 @synchronized 是对mutex递归锁的封装
  3.8.2 @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
  3.8.3 示例代码
@synchronized(obj){
}

练习代码git

https://gitee.com/tianmingfu/thread-synchronization.git

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

推荐阅读更多精彩内容

  • 应用程序里面多个线程的存在引发了多个执行线程安全访问资源的潜在问题。两个线程同时修改同一资源有可能以意想不到的方式...
    搬砖的crystal阅读 616评论 0 1
  • 1. 什么情况下会有线程隐患? 我们在使用多线程技术带来的便利的同时,也需要考虑下多线程所带来的隐患。比如,我们可...
    沉江小鱼阅读 806评论 0 11
  • iOS 的锁有以下 10 种:OSSpinLock、os_unfair_lock、dispatch_semapho...
    阿斯兰iOS阅读 470评论 0 3
  • 一、iOS中常见的多线程方案 要解释多线程,需要先解释进程和线程之间的区别。线程才是程序真正的执行单元,一个进程可...
    小豆豆苗阅读 782评论 0 0
  • 深入浅出iOS多线程(一)——线程的概念深入浅出iOS多线程(二)——pthraed和NSThread的使用深入浅...
    struggle3g阅读 1,124评论 0 5