美文网首页ios底层原理
多线程编程小结

多线程编程小结

作者: 云无心 | 来源:发表于2020-05-17 15:23 被阅读0次

    简单回顾

    image.png
    NSThread
    NSThread的常见用法
    - (void)创建及使用 {
        // 创建
        [NSThread detachNewThreadWithBlock:^{
            [self doSomething];
        }];
        [NSThread detachNewThreadSelector:@selector(doSomething) toTarget:self withObject:nil];
        NSThread *threadOne = [[NSThread alloc] initWithBlock:^{
            [self doSomething];
        }];
        NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething) object:nil];
        
        // name
        [threadTwo setName:@"threadTwo"];
        // 程使用栈区大小,默认是512K
        [threadTwo setStackSize:512];
        // iOS8以后推荐使用qualityOfService属性,通过量化的优先级枚举值来设置
        [threadTwo setQualityOfService:NSQualityOfServiceUserInteractive];
        // 线程优先级设置 iOS8以前使用 给当前线程设定优先级,调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高。
        [threadTwo setThreadPriority:[NSThread mainThread].threadPriority]; //线程优先级设置 iOS8以前使用
        // 状态
        [threadTwo isExecuting];
        [threadTwo isCancelled];
        [threadTwo isFinished];
        [threadTwo isMainThread];
        
        //开启
        [threadTwo start];
        //取消线程并不会马上停止并退出线程,仅仅只作(线程是否需要退出)状态记录
        [threadOne cancel];
        
        // 当前线程是否子线程
        [NSThread isMultiThreaded];
        // 获取当前线程、主线程
        [NSThread currentThread];
        [NSThread mainThread];
        
        [NSThread exit];//停止方法会立即终止除主线程以外所有线程(无论是否在执行任务)并退出,需要在掌控所有线程状态的情况下调用此方法,否则可能会导致内存问题。
        // sleep
        [NSThread sleepForTimeInterval:1.0];
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
        //  给当前线程设定优先级,调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高。
        [NSThread setThreadPriority:1.0];
        
        // 线程的调用都会有函数的调用函数的调用就会有栈返回地址的记录,在这里返回的是函 数调用返回的虚拟地址,说白了就是在该线程中函数调用的虚拟地址的数组
        NSArray *addressArray = [NSThread callStackReturnAddresses];
        // 同上面的方法一样,只不过返回的是该线程调用函数的名字数字
        NSArray *nameArray = [NSThread callStackSymbols];
    }
    

    线程切换

    - (void)切换线程 {
        
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(延时操作) object:nil];
        [thread setName:@"切换线程的thread"];
        [thread start];
    }
    - (void)延时操作 {
        NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0f];
        [self performSelectorOnMainThread:@selector(主线程更新视图) withObject:nil waitUntilDone:YES];
    }
    - (void)主线程更新视图 {
    
        NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
    }
    
    • NSQualityOfServiceUserInteractive:最高优先级,用于用户交互事件
    • NSQualityOfServiceUserInitiated:次高优先级,用于用户需要马上执行的事件
    • NSQualityOfServiceDefault:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
    • NSQualityOfServiceUtility:普通优先级,用于普通任务
    • NSQualityOfServiceBackground:最低优先级,用于不重要的任务

    NSOperation

    常见用法
    - (void)createInvocationOperation {
        
        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething) object:nil];
        operation.completionBlock = ^{
            
            NSLog(@"completionBlock --%s-- IN thread:%@",__func__,[NSThread currentThread]);
        };
        [operation start];
        
    }
    #pragma mark -
    - (void)切换线程 {
        
        NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self
                                                                               selector:@selector(延时操作)
                                                                                 object:nil];
           
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        [queue setName:@"OperationQueue"];
        [queue setQualityOfService:NSQualityOfServiceUserInteractive];
        [queue setMaxConcurrentOperationCount:5];
        [queue addOperation:operation];
    }
    - (void)延时操作 {
        
        NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0f];
        NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self
                                                                               selector:@selector(主线程更新视图)
                                                                                 object:nil];
        [[NSOperationQueue mainQueue] addOperation:operation];
        
    }
    - (void)主线程更新视图 {
    
        NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
    }
    
    
    #pragma mark - NSBlockOperation
    - (void)createBlockOperation {
        // 追加操作并发执行
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
            NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);
        }];
    
        [operation addExecutionBlock:^() {
            NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
        }];
        
        [operation addExecutionBlock:^() {
            NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
        }];
    
        [operation addExecutionBlock:^() {
            NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
        }];
        
        // 开始执行任务
        [operation start];
        
    }
    
    
    - (void)doSomething {
        
        NSLog(@"doSomething IN thread:%@",[NSThread currentThread]);
    }
    
    添加任务依赖
    - (void)创建依赖 {
        //创建操作队列
        NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
        //创建最后一个操作
        NSBlockOperation *lastBlockOperation=[NSBlockOperation blockOperationWithBlock:^{
            [NSThread sleepForTimeInterval:5.f];
            NSLog(@"开始的任务");
        }];
        for (int i=0; i<5; ++i) {
            //创建多线程操作
            NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:^{
                [NSThread sleepForTimeInterval:i];
                NSLog(@"第%d个任务",i);
            }];
            //设置依赖操作为最后一个操作
            [blockOperation addDependency:lastBlockOperation];
            [operationQueue addOperation:blockOperation];
            
        }
        //将最后一个操作加入线程队列
        [operationQueue addOperation:lastBlockOperation];
    // log    
    // 开始的任务
    // 第0个任务
    // 第1个任务
    // 第2个任务
    // 第3个任务
    // 第4个任务
    }
    
    任务执行状态控制(kvc)
    isReady
    isExecuting
    isFinished
    isCancelled
    如果只重写了main,底层控制变更任务执行完成状态,以及任务退出
    如果重写了start,自行控制任务状态
    
    最大并发量 MaxConcurrentOperationCount

    队列里可以加入很多个NSOperation, 可以把NSOperationQueue看作一个线程池,可往线程池中添加操作(NSOperation)到队列中。线程池中的线程可看作消费者,从队列中取走操作,并执行它。线程池中的线程数,也就是并发操作数。默认情况下是-1,-1表示没有限制,这样会同时运行队列中的全部的操作。

    自定义 NSOperation
    自定义非并发NSOperation

    Methods to Override
    For non-concurrent operations, you typically override only one method: main
    Into this method, you place the code needed to perform the given task.

    在官方文档中指出,非并发任务,直接将需要执行的任务放在main方法中,然后直接调用即可。 这样直接调用main方法会存在一个问题,由于没有实现finished属性,所以获取finished属性时,只会返回NO,而且任务加入到队列后,不会被删除,另外任务执行完后,回调也不会被执行,所以最好不要只实现一个main方法来使用。

    - (void)main {
        // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
        @autoreleasepool {
            if (self.isCancelled) {
                return;
            }
            
            // 耗时操作
            
            if (self.isCancelled) {
                return;
            }
            if (self.responseBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.responseBlock();
                });
            }
        }
    }
    
    自定义并发NSOperation

    Methods to Override
    If you are creating a concurrent operation, you need to override the following methods and properties at a minimum:
    start
    asynchronous
    executing
    finished

    • 重写NSOperation的main方法,该方法会作为NSOperation所启动线程的执行体
      在 operation 的 main 方法里面,必须提供 autorelease pool,因为你的 operation 完成后需要销毁
    • 实现KVO机制,一旦你的 operation 开始了,必须通过 KVO,告诉所有的监听者,现在该operation的执行状态。(即修改isFinished,isExecuting的值)
    • 重载 isExecuting方法和isFinished方法。在这两个方法中,必须返回一个线程安全值(通常是个BOOL值),这个值可以在 operation 中进行操作。
    • 重写isAsynchronous(或者isConcurrent)
    @interface FSConOperation (){
    BOOL _isFinished;
    BOOL _isExecuting;
    }
    
    @end
    
    @implementation FSConOperation
    
    - (instancetype)initWithUrl:(NSString *)url completion:(dispatch_block_t)completion {
        if (self = [super init]) {
            self.url = url;
            self.responseBlock = completion;
        }
    
        return self;
    }
    
    // 必须重写这个主方法
    - (void)main {
    // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
        @autoreleasepool {
            // 提供一个变量标识,来表示我们需要执行的操作是否完成了,当然,没开始执行之前,为NO
            BOOL taskIsFinished = NO;
            UIImage *image = nil;
    
            // while 保证:只有当没有执行完成和没有被取消,才执行我们自定义的相应操作
            while (taskIsFinished == NO && [self isCancelled] == NO){
                // 获取图片数据
                NSURL *url = [NSURL URLWithString:self.url];
                NSData *imageData = [NSData dataWithContentsOfURL:url];
                image = [UIImage imageWithData:imageData];
                NSLog(@"Current Thread = %@", [NSThread currentThread]);
    
                // 这里,设置我们相应的操作都已经完成,后面就是要通知KVO我们的操作完成了。
    
                taskIsFinished = YES;
            }
    
            // KVO 生成通知,告诉其他线程,该operation 执行完了
            [self willChangeValueForKey:@"isFinished"];
            [self willChangeValueForKey:@"isExecuting"];
            _isFinished = YES;
            _isExecuting = NO;
            [self didChangeValueForKey:@"isFinished"];
            [self didChangeValueForKey:@"isExecuting"];
            
            if (self.responseBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.responseBlock();
                });
            }
        }
    }
    
    - (void)start {
        // 如果我们取消了在开始之前,我们就立即返回并生成所需的KVO通知
        if ([self isCancelled]){
            // 我们取消了该 operation,那么就要告诉KVO,该operation已经执行完成(isFinished)
            // 这样,调用的队列(或者线程)会继续执行。
            [self willChangeValueForKey:@"isFinished"];
            _isFinished = NO;
            [self didChangeValueForKey:@"isFinished"];
        } else {
            // 没有取消,那就要告诉KVO,该队列开始执行了(isExecuting)!那么,就会调用main方法,进行同步执行。
            [self willChangeValueForKey:@"isExecuting"];
            _isExecuting = YES;
            [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
            [self didChangeValueForKey:@"isExecuting"];
        }
    }
    
    - (BOOL)isExecuting {
        return _isExecuting;
    }
    
    - (BOOL)isFinished {
        return _isFinished;
    }
    // 与自定义同步NSOperation不同的是,必须要实现下面的方法
    #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0
    - (BOOL)isConcurrent {
        return YES;
    }
    
    #else
    
    - (BOOL)isAsynchronous {
        return YES;
    }
    #endif
    
    @end
    

    使用

        FSConOperation *operation = [[FSConOperation alloc] initWithUrl:@"url" completion:^{
            //do completion
        }];
        [operation start];
    

    GCD

    了解概念
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    • 有4个术语比较容易混淆:同步、异步、并发、串行

    • 同步和异步主要影响:能不能开启新的线程
      同步:在当前线程中执行任务,不具备开启新线程的能力
      异步:在新的线程中执行任务,具备开启新线程的能力

    • 并发和串行主要影响:任务的执行方式
      并发:多个任务并发(同时)执行
      串行:一个(当前)任务执行完毕后,再执行下一个任务

    操作方式 并发队列 串行队列(手动创建) 串行队列(主队列)
    同步(sync) 没有开启新线程,串行执行 没有开启新线程,串行执行 没有开启新线程,串行执行
    异步(async) 有开启新线程,并发执行 有开启新线程,串行执行 没有开启新线程,串行执行
    • 在当前串行队列中使用sync函数添加任务,会卡住当前的串行队列(产生死锁)。例如在viewDidLoad中,在主线程sync插入任务A。因为FIFO,主队列中需要viewDidLoad完成后才会执行任务A,但是因为sync任务A需要立即执行。而当前队列正在执行viewDidLoad。这样viewDidLoad等待任务A,任务A等待viewDidLoad,造成死锁
    GCD应用
    用 dispatch_async 处理后台任务
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
            UIImage *overlayImage = [self faceOverlayImageFromImage:_image];
            dispatch_async(dispatch_get_main_queue(), ^{ // 2
                [self fadeInNewImage:overlayImage]; // 3
            });
        });
    
    使用 dispatch_after 延后工作

    一旦 dispatch_after 返回也就不能取消它,最好主线程操作

        double delayInSeconds = 1.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1 
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2 
            if (!count) {
                [self.navigationItem setPrompt:@"Add photos with faces to Googlyify them!"];
            } else {
                [self.navigationItem setPrompt:nil];
            }
        });
    
    使用group处理依赖
    - (void)group {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_group_enter(group);
        dispatch_group_async(group, queue, ^{
            [NSThread sleepForTimeInterval:1];
            NSLog(@"group1");
            dispatch_group_leave(group);
        });
        dispatch_group_enter(group);
        dispatch_group_async(group, queue, ^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"group2");
            dispatch_group_leave(group);
        });
        dispatch_group_enter(group);
        dispatch_group_async(group, queue, ^{
            [NSThread sleepForTimeInterval:3];
            NSLog(@"group3");
            dispatch_group_leave(group);
        });
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"updateUi");
        });
    }
    
    使用barrier

    queue必须是DISPATCH_QUEUE_CONCURRENT类型的.queue如果是global或者不是DISPATCH_QUEUE_CONCURRENT类型无效

    - (void)barrier {
        dispatch_queue_t queue = dispatch_queue_create("test.barrier.com", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"dispatch_async1");
        });
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:4];
            NSLog(@"dispatch_async2");
        });
        dispatch_barrier_async(queue, ^{
            NSLog(@"dispatch_barrier_async");
            [NSThread sleepForTimeInterval:4];
           
        });
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:1];
            NSLog(@"dispatch_async3");
        });
    }
    
    使用apply
    - (void)dispatchApplyTest {
        //生成全局队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        
        /*! dispatch_apply函数说明
         *
         *  @brief  dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API
         *         该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等到全部的处理执行结束
         *
         *  @param 10    指定重复次数  指定10次
         *  @param queue 追加对象的Dispatch Queue
         *  @param index 带有参数的Block, index的作用是为了按执行的顺序区分各个Block
         *
         */
        dispatch_apply(10, queue, ^(size_t index) {
            NSLog(@"%zu", index);
        });
        NSLog(@"done");
        
        /*!
         *  @brief  输出结果
         *
         2016-02-25 19:24:39.102 dispatch_apply测试[2985:165004] 0
         2016-02-25 19:24:39.102 dispatch_apply测试[2985:165086] 1
         2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 4
         2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 5
         2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 6
         2016-02-25 19:24:39.103 dispatch_apply测试[2985:165088] 3
         2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 7
         2016-02-25 19:24:39.105 dispatch_apply测试[2985:165004] 8
         2016-02-25 19:24:39.105 dispatch_apply测试[2985:165004] 9
         2016-02-25 19:24:39.102 dispatch_apply测试[2985:165087] 2
         2016-02-25 19:24:39.105 dispatch_apply测试[2985:165004] done
         *  !!!因为在Global Dispatch Queue中执行,所以各个处理的执行时间不定
         但done一定会输出在最后的位置,因为dispatch_apply函数会等待所以的处理结束
         */
    }
    

    常见锁的使用:

    为什么需要锁

    未加锁
    已加锁

    锁的种类

    OSSpinLock
    • OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
    • 目前已经不再安全,可能会出现优先级反转问题
    • 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
    • 需要导入头文件#import <libkern/OSAtomic.h>

    具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。

    - (void)用法介绍 {
        //init
        OSSpinLock lock = OS_SPINLOCK_INIT;
        // 尝试加锁,无需等待,直接加锁,返回YES.如果需要等待就不加锁,返回NO,
        BOOL result = OSSpinLockTry(&lock);
        // 加锁
        OSSpinLockLock(&lock);
        // 解锁
        OSSpinLockUnlock(&_moneyLock);
    }
    
    os_unfair_lock
    • os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持
    • 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
    • 需要导入头文件#import <os/lock.h>
    - (void)用法介绍 {
        //init
        os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
        // 尝试加锁,无需等待,直接加锁,返回YES.如果需要等待就不加锁,返回NO,
        BOOL result = os_unfair_lock_trylock(&lock);
        // 加锁
        os_unfair_lock_lock(&lock);
        // 解锁
        os_unfair_lock_unlock(&_moneyLock);
    }
    
    pthread_mutex
    • mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
    • 需要导入头文件#import <pthread.h>
    - (void)用法介绍 {
        //初始化属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        /*
         * Mutex type attributes
        #define PTHREAD_MUTEX_NORMAL        0
        #define PTHREAD_MUTEX_ERRORCHECK    1
        #define PTHREAD_MUTEX_RECURSIVE        2
        #define PTHREAD_MUTEX_DEFAULT        PTHREAD_MUTEX_NORMAL
        */
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
        
        //初始化锁
        pthread_mutex_t lock;
        pthread_mutex_init(&lock, &attr);
        // 尝试加锁
        pthread_mutex_trylock(&lock);
        // 加锁
        pthread_mutex_lock(&lock);
        // 解锁
        pthread_mutex_unlock(&lock);
        // 销毁资源
        pthread_mutexattr_destroy(&attr);
        pthread_mutex_destroy(&lock);
    }
    
    pthread_mutex递归锁
    #pragma mark - 递归锁
    - (void)__initRecursiveMutex:(pthread_mutex_t *)mutex
    {
        // 递归锁:允许同一个线程对一把锁进行重复加锁
        
        // 初始化属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        // 初始化锁
        pthread_mutex_init(mutex, &attr);
        // 销毁属性
        pthread_mutexattr_destroy(&attr);
    }
    // 同一个线程可以重复加锁
    - (void)recursiveTest {
        
        pthread_mutex_lock(&_recursive_mutex);
        
        NSLog(@"%s", __func__);
        
        static int count = 0;
        if (count < 10) {
            count++;
            [self recursiveTest];
        }
        
        pthread_mutex_unlock(&_recursive_mutex);
    }
    
    pthread_mutex条件锁
    - (void)用法介绍 {
        // 初始化锁
        pthread_mutex_t mutex;
        pthread_mutex_init(&mutex, NULL);
        // 初始化条件
        pthread_cond_t conditon;
        pthread_cond_init(&conditon, NULL);
        // 等待条件,进入休眠,放开锁。唤醒后再次对mutex加锁
        pthread_cond_wait(&conditon, &mutex);
        // 激活一个等待条件对的线程
        pthread_cond_signal(&conditon);
        // 激活所有等待条件得到线程
        pthread_cond_broadcast(&conditon);
        //销毁资源
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&conditon);
    }
    
    - (void)coditionTest {
        [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
        [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
    }
    // 删除数组中的元素
    - (void)__remove {
        pthread_mutex_lock(&_mutex);
        NSLog(@"__remove - begin");
        if (self.data.count == 0) {
            // 等待
            pthread_cond_wait(&_conditon, &_mutex);
        }
        [self.data removeLastObject];
        NSLog(@"删除了元素");
        pthread_mutex_unlock(&_mutex);
    }
    // 往数组中添加元素
    - (void)__add {
        pthread_mutex_lock(&_mutex);
        sleep(1);
        
        [self.data addObject:@"Test"];
        NSLog(@"添加了元素");
        pthread_cond_signal(&_conditon);
        pthread_mutex_unlock(&_mutex);
    }
    
    NSLock & NSRecursiveLock

    NSLock是对mutex的普通锁封装

    NSLOCK
    NSRecursiveLOCK
    - (void)用法介绍 {
        NSLock *lock = [[NSLock alloc] init];
        [lock tryLock];
        [lock lockBeforeDate:[NSDate distantFuture]];
        
        [lock lock];
        [lock unlock];
        
        NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
        [recursiveLock tryLock];
        [recursiveLock lockBeforeDate:[NSDate distantFuture]];
        
        [recursiveLock lock];
        [recursiveLock unlock];
    }
    
    NSCondition

    NSCondition是对mutex和cond的封装

    - (void)用法介绍 {
        NSCondition *condition = [[NSCondition alloc] init];
        [condition wait];
        [condition waitUntilDate:[NSDate distantFuture]];
        [condition signal];
        [condition broadcast];
        
        [condition lock];
        [condition unlock];
    }
    
    - (void)coditionTest {
        [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
        [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
    }
    
    - (void)__remove {
        [self.condition lock];
        NSLog(@"__remove - begin");
        if (self.data.count == 0) {
            // 等待
            [self.condition wait];
        }
        [self.data removeLastObject];
        NSLog(@"删除了元素");
        [self.condition unlock];
    }
    
    - (void)__add {
        [self.condition lock];
        sleep(1);
        [self.data addObject:@"Test"];
        NSLog(@"添加了元素");
        // 信号
        [self.condition signal];
        sleep(2);
        [self.condition unlock];
    }
    
    NSConditionLock

    NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值condition

    - (void)用法介绍 {
        NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:1];
        NSInteger cod = [lock condition];//readOnly
        [lock lock];//不管cod,直接加锁
        [lock lockWhenCondition:1];
        [lock tryLock];
        [lock tryLockWhenCondition:1];
        [lock lockBeforeDate:[NSDate distantFuture]];
        [lock lockWhenCondition:1 beforeDate:[NSDate distantFuture]];
        [lock unlock];//不管cod,直接解锁
        [lock unlockWithCondition:1];
    }
    
    - (void)coditionLockTest {
        [[[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];
    }
    
    dispatch_semaphore
    • semaphore叫做”信号量”
    • 信号量的初始值,可以用来控制线程并发访问的最大数量
    • 信号量的初始值设为1,代表同时只允许1条线程访问资源,保证线程同步
    • 相关代码在libdispatch
    - (void)用法介绍 {
        // 初始化 信号量=1,可以控制并发数量
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
        // 信号量-1
        // 如果信号量>0,继续执行。如果信号量<=0,当前线程休眠
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//time=等待时间
        // 信号量+1
        dispatch_semaphore_signal(self.moneySemaphore);
    }
    
    - (void)__发红包 {
        dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
        [super __发红包];
        dispatch_semaphore_signal(self.moneySemaphore);
    }
    
    @synchronized
    • @synchronized是对mutex递归锁的封装
    • 源码查看:objc4中的objc-sync.mm文件
    • @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
    @synchronized(self) {
    
    }
    
    dispatch_queue(DISPATCH_QUEUE_SERIAL)

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

    - (void)__卖票 {
        dispatch_sync(self.moneyQueue, ^{
            [super __卖票];
        });
    }
    
    汇编指令暂存

    c 下个断点
    s step 一行OC代码
    si stepinstruction 一行指令 汇编
    nexti 函数
    j 跳转 ne 条件
    回车为上一个指令

    性能对比
    image.png

    性能从高到低排序

    os_unfair_lock(iOS10开始支持)
    OSSpinLock(优先级反转)
    dispatch_semaphore
    pthread_mutex
    dispatch_queue(DISPATCH_QUEUE_SERIAL)
    NSLock
    NSCondition
    pthread_mutex(recursive)
    NSRecursiveLock
    NSConditionLock
    @synchronized
    

    互斥和自旋

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

      • 预计线程等待锁的时间很短
      • 加锁的代码(临界区)经常被调用,但竞争情况很少发生
      • CPU资源不紧张
      • 多核处理器
    • 什么情况使用互斥锁比较划算?

      • 预计线程等待锁的时间较长
      • 单核处理器
      • 临界区有IO操作
      • 临界区代码复杂或者循环量大
      • 临界区竞争非常激烈

    读写锁(I/O)

    多读单写:

    • 同一时间,只能有1个线程进行写的操作
    • 同一时间,允许有多个线程进行读的操作
    • 同一时间,不允许既有写的操作,又有读的操作
    pthread_rwlock_t

    等待锁的线程会进入休眠

    - (void)用法介绍 {
        
        pthread_rwlock_t lock;
        pthread_rwlock_init(&lock,NULL);
        
        pthread_rwlock_rdlock(&lock);
        pthread_rwlock_tryrdlock(&lock);
        pthread_rwlock_wrlock(&lock);
        pthread_rwlock_trywrlock(&lock);
        pthread_rwlock_unlock(&lock);
        
        pthread_rwlock_destroy(&lock);
    }
    - (void)read {
        pthread_rwlock_rdlock(&_lock);
        sleep(1);
        NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
        pthread_rwlock_unlock(&_lock);
    }
    - (void)write {
        pthread_rwlock_wrlock(&_lock);
        sleep(1);
        NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
        pthread_rwlock_unlock(&_lock);
    }
    
    dispatch_barrier_async

    这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的
    如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果

    - (void)read {
        dispatch_async(self.queue, ^{
            sleep(1);
            NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
        });
    }
    - (void)write 
        dispatch_barrier_async(self.queue, ^{
            sleep(1);
            NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
        });
    }
    

    GCD原理:

    其他:

    参考
    iOS多线程编程的几种方式
    iOS进阶之多线程--NSThread详解
    自定义NSOperation
    彻底了解NSOperation的自定义
    深入理解GCD之dispatch_group
    GCD 深入理解
    GNUstep
    libdispatch
    Jason's Blog,@synchronized 是递归锁
    objc4
    不再安全的 OSSpinLock

    相关文章

      网友评论

        本文标题:多线程编程小结

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