美文网首页
多线程二

多线程二

作者: 前年的邂逅_Jerry | 来源:发表于2017-11-19 20:33 被阅读43次

    一、死锁

    • 最容易导致死锁的一个情况
      信号锁 + 互斥锁 ,没处理好,导致死锁,NSConditionLock 解决死锁问题。
      例1:互斥锁执行两次lock操作[self.lock lock];
      例2:信号锁+互斥锁
    @interface MyLock()
    
    @property(nonatomic, strong) NSLock * lock;
    
    @property (nonatomic, strong) dispatch_semaphore_t semaphore;
    
    @property (nonatomic, assign) NSInteger count;
    @end
    
    @implementation MyLock
    - (void)dealloc{
        NSLog(@"%s",__func__);
    }
    - (instancetype)init{
        self = [super init];
        if (self) {
            //创建线程
            self.lock = [[NSLock alloc] init];
            [NSThread detachNewThreadSelector:@selector(signalWrite) toTarget:self withObject:nil];
            [NSThread detachNewThreadSelector:@selector(signalRead) toTarget:self withObject:nil];
            
            self.semaphore = dispatch_semaphore_create(0);
        }
        return self;
    }
    
    - (void)signalWrite{
        while (1) {
            [self.lock lock];
            NSLog(@"%ld",self.count);
            if (self.count >= 10) {
                dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
            }
            self.count ++;
            NSLog(@"%s",__func__);
            [self.lock unlock];
        }
    }
    - (void)signalRead{
        while (1) {
            [self.lock lock];
            NSLog(@"%ld",self.count);
            if (self.count >= 10) {
                self.count --;
                dispatch_semaphore_signal(_semaphore);
            }else{
                self.count ++;
            }
            NSLog(@"%s",__func__);
            [self.lock  unlock];
        }
    }
    
    • 使用条件锁解决死锁问题 信号锁+互斥锁 = 条件锁
    @interface MyConditionLock()
    @property (nonatomic,strong) NSConditionLock * conditionLock;
    @property (nonatomic, assign) NSInteger count;
    @property (nonatomic, strong) NSCondition * condition;
    @end
    @implementation MyConditionLock
    - (instancetype)init{
        self = [super init];
        if (self) {
            self.condition = [[NSCondition alloc] init];
            [NSThread detachNewThreadSelector:@selector(thread1) toTarget:self withObject:nil];
            [NSThread detachNewThreadSelector:@selector(thread2) toTarget:self withObject:nil];
            self.count = 0;
        }
        return self;
    }
    
    - (void)thread1{
        while (1) {
            [self.condition lock];
            if (self.count >= 10) {
                //这里的wait相当于释放了锁
                [self.condition wait];
            }
            self.count ++;
            NSLog(@"%s = %ld",__func__,self.count);
            [self.condition unlock];
        }
    }
    - (void)thread2{
        while (1) {
            [self.condition lock];
            if (self.count >= 10) {
                self.count --;
                [self.condition signal];
            }else{
                self.count ++;
            }
            NSLog(@"%s = %ld",__func__,self.count);
            [self.condition unlock];
        }
    }
    @end
    
    • 条件锁原理
    @interface ConditionTheory(){
        pthread_mutex_t mutex_t;
        pthread_cond_t cond_t;
        int _count;
    }
    @end
    @implementation ConditionTheory
    - (instancetype)init{
        self = [super init];
        if (self) {
            pthread_mutex_init(&mutex_t,0);
            pthread_cond_init(&cond_t, 0);
            [NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
            [NSThread detachNewThreadSelector:@selector(threadTwo) toTarget:self withObject:nil];
        }
        return self;
    }
    #pragma mark - C用法条件量 (原理)
    - (void)threadOne{   
        while (1) {
            pthread_mutex_lock(&mutex_t);
            NSLog(@"signalLockWrite lock");
            if (_count >= 10) {
                NSLog(@"空间满了,等待中--%d", _count);
                // [conditionLock wait];
                pthread_cond_wait(&cond_t, &mutex_t);
            }
            _count++;
            pthread_mutex_unlock(&mutex_t);
            NSLog(@"signalLockWrite unlock");
        }
    }
    - (void)threadTwo{   
        while (1) {
            NSLog(@"signalLockRead lock");
            pthread_mutex_lock(&mutex_t);
            if (_count > 10) {
                _count--;
                NSLog(@"释放空间~");
                // [conditionLock signal];
                pthread_cond_signal(&cond_t);
            }else {
                _count++;
            }
            NSLog(@"signalLockRead lock");
            pthread_mutex_unlock(&mutex_t);
        }
    }
    

    二、多线程GCD

    1、main_dispatch_queue(系统提供的)

    获取主队列
    全局性的串性队列,所有的ui操作相关的任务都应该放在这个queue里面,在主线程中执行通过宏dispatch_get_main_queque()创建任务

    2、globlal_dispatch_queue(系统提供的)

    并发执行多个任务队列,但是执行完成的顺序是随机的。一般后台执行的任务放在这个队列里面。
    获取队列,通过函数来设置任务的优先级:dispatch_get_global_queue(0,0),第一个参数,为默认的优先级(四个优先级),第二个参数为预留的。

    3、自定义的dispatch queque(可以是串型队列,也可以是并行队列)

    创建串型队列,自定义的queue一般和ui操作无关,用于同步访问特定的资源或数据,比如多线程对同一文件的写入。
    dispatch_queue_create("SerialQueue",DISPATCH_QUEUE_SERIAL),第一个是队列名称,第二个参数为串形队列。DISPATCH_QUEUE_CONCURRENT并形队列

    4、提交任务到dispatch_queue

    block 里面是任务

    • 同步任务
      void dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);
    • 异步任务
      void dispatch_async(dispatch_queue_t queue,dispatch_block_t block);
      将耗时的操作提交给非主线程,更新界面提交给主线程,如下:
    dispatch_async(dispatch_get_golabal_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
            //耗时操作,解析数据
            dispatch_async(dispatch_get_main_queue(),^{
                //更新界面
            });
        });
    

    5、组队列

    • 组队列通知,用于组队列全部安全产生的回掉。
        dispatch_group_t group = dispatch_group_create();
        dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_async(group, queue1, ^{
            NSLog(@"one_task");
            sleep(1);
            NSLog(@"one_task over");
        });
        dispatch_group_async(group, queue1, ^{
            NSLog(@"2_task");
            sleep(1);
            NSLog(@"2_task over");
        });
        dispatch_group_async(group, queue1, ^{
            NSLog(@"3_task");
            sleep(1);
            NSLog(@"3_task over");
        });
        dispatch_group_notify(group, queue1, ^{
            //当所有队列完成是,才会走这里
            NSLog(@"nofify no task");
        });
        dispatch_group_async(group, queue1, ^{
            NSLog(@"4_task");
            sleep(1);
            NSLog(@"4_task over");
        });
    
    • AFNetworking 中会有类似的应用。
      向组队列中添加空任务:dispatch_group_enter(group);,但一定要移除:dispatch_group_leave(group)。否则不会执行dispatch_group_notify

    6、设置屏障

    • 应用场景:当某些任务完成之后才执行另外一条任务。
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            NSLog(@"one tast");
            sleep(1);
            NSLog(@"one task finished");
        });
        dispatch_async(queue, ^{
            NSLog(@"two tast");
            sleep(1);
            NSLog(@"two task finished");
        });
        //设置屏障
        dispatch_barrier_async(queue, ^{
            NSLog(@"barrier");
        });
        dispatch_async(queue, ^{
            NSLog(@"three tast");
            sleep(1);
            NSLog(@"three task finished");
        });
    

    运行结果

    one tast
    two tast
    one task finished
    two task finished
    barrier
    three tast
    three task finished
    

    7、应用多次,多次迭代

    • 并行应用多次,系统随机执行任务
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_apply(5, queue, ^(size_t count) {
            NSLog(@"%d==%@",(int)count,[NSThread currentThread]);
        });
    

    运行结果:

    0==<NSThread: 0x60c000078580>{number = 1, name = main}
    2==<NSThread: 0x604000468540>{number = 4, name = (null)}
    1==<NSThread: 0x608000263900>{number = 3, name = (null)}
    3==<NSThread: 0x604000468600>{number = 5, name = (null)}
    4==<NSThread: 0x60c000078580>{number = 1, name = main}
    
    • 串型运行多次
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        dispatch_apply(5, queue, ^(size_t count) {
            NSLog(@"%d==%@",(int)count,[NSThread currentThread]);
        });
    

    运行结果:

    0==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
    1==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
    2==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
    3==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
    4==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
    

    8、延时

        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2.2*NSEC_PER_SEC);
        dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
            NSLog(@"runloop = %@",runLoop);
            [self performSelect];
        });
    

    9、计时器

        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        //1秒钟开始一次,误差是一秒秒
        dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC , 1 * NSEC_PER_SEC);
        //设置事件
        dispatch_source_set_event_handler(source, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
        dispatch_resume(source);
    

    三、注意:

    • 1、在主线程开主线串行队列,会导致奔溃。
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        dispatch_sync(dispatch_get_main_queue(), ^{
           //  //会等待主线程完成,而主线程不会完成
            NSLog(@"dispatch_sync");
        });
        [self time];
    }
    
    • 2、dispatch_sync的官方文档中有这么一句话:
      As an optimization, this function invokes the block on the current thread when possible.
      意思是作为优化手段,如果可以,block会直接在当前线程中执行。
      然后写了几段代码测试了下:
    dispatch_async(_myQueue1, ^{  
      
        NSLog(@"1 start!");  
        sleep(1);  
        NSLog(@"1 stop!");  
      
        dispatch_async(_myQueue2, ^{  
            NSLog(@"2 start!");  
            sleep(2);  
            NSLog(@"2 stop!");  
        });  
      
        dispatch_sync(_myQueue2, ^{  
            NSLog(@"3 start!");  
            sleep(1);  
            NSLog(@"3 stop!");  
        });  
    });  
    

    如果_myQueue1,_myQueue2都是自己创建的队列,那3确实是与1在同一线程中执行,2在另外一个线程中执行。但是3虽然与2不在一个线程中,但是由于在同一队列里,所以依然要等待2执行完才能执行3,当前线程会被阻塞直到2、3都执行完。

        如果令_myQueue1 = dispatch_get_main_queue(),_myQueue2是自己创建的队列,3就会在主线程中执行。也就是说在主线程中调用dispatch_sync往子线程派发任务,任务会直接在主线程执行。
    
        但如果_myQueue1是自己创建的队列,_myQueue2 = dispatch_get_main_queue(),也就是说在子线程中调用dispatch_sync往主线程派发任务,GCD不会做这种优化,任务会如你所愿在主线程中执行。
    

    以上结论仅仅是简单代码测试得到,有可能在复杂环境下苹果有更为复杂的线程调度策略。大家在使用同步派发时需要自己注意下。

    - (void)viewDidLoad {
        [super viewDidLoad];
        dispatch_queue_t _myQueue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
        dispatch_queue_t _myQueue1 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(_myQueue1, ^{
            
            NSLog(@"1 start!");
            sleep(1);
            NSLog(@"1 stop!");
            
            dispatch_async(_myQueue2, ^{
                NSLog(@"2 start!");
                sleep(2);
                NSLog(@"2 stop!");
            });
            
            dispatch_sync(_myQueue2, ^{
                NSLog(@"3 start!");
                sleep(12);
                NSLog(@"3 stop!");
            });
        });
    }
    结果:
    2018-04-19 00:09:57.249 Demo1[2096:799609] 1 start!
    2018-04-19 00:09:58.254 Demo1[2096:799609] 1 stop!
    2018-04-19 00:09:58.254 Demo1[2096:799609] 3 start!
    2018-04-19 00:09:58.254 Demo1[2096:799588] 2 start!
    2018-04-19 00:10:00.260 Demo1[2096:799588] 2 stop!
    2018-04-19 00:10:10.255 Demo1[2096:799609] 3 stop!
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        dispatch_queue_t _myQueue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
        dispatch_queue_t _myQueue1 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(_myQueue1, ^{
            
            NSLog(@"1 start!");
            sleep(1);
            NSLog(@"1 stop!");
            
            dispatch_sync(_myQueue2, ^{
                NSLog(@"2 start!");
                sleep(6);
                NSLog(@"2 stop!");
            });
            
            dispatch_async(_myQueue2, ^{
                NSLog(@"3 start!");
                sleep(2);
                NSLog(@"3 stop!");
            });
        });
    }
    结果:
    2018-04-18 23:32:44.521 Demo1[1936:681182] 1 start!
    2018-04-18 23:32:45.527 Demo1[1936:681182] 1 stop!
    2018-04-18 23:32:45.527 Demo1[1936:681182] 2 start!
    2018-04-18 23:32:51.530 Demo1[1936:681182] 2 stop!
    2018-04-18 23:32:51.530 Demo1[1936:681182] 3 start!
    2018-04-18 23:32:53.532 Demo1[1936:681182] 3 stop!
    

    相关文章

      网友评论

          本文标题:多线程二

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