美文网首页 iOS进阶之面试题iOS
iOS中的线程同步方案

iOS中的线程同步方案

作者: 小小小蚍蜉 | 来源:发表于2019-02-17 23:41 被阅读26次

    前提简述:

    常用的线程方案有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操作

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

    临界区竞争非常激烈

    相关文章

      网友评论

        本文标题:iOS中的线程同步方案

        本文链接:https://www.haomeiwen.com/subject/yxvneqtx.html