美文网首页版本特性
dispatch_semaphore_t异步任务同步执行和线程加

dispatch_semaphore_t异步任务同步执行和线程加

作者: LeeRich | 来源:发表于2018-03-15 23:46 被阅读401次
    作者:foolishBoy
    链接:https://www.jianshu.com/p/c815ad360a2a
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    在UNIX环境下,多线程同步的技术有mutex、condition variable、semaphore、RW Lock、spin Lock等。在iOS平台上,可以使用dispatch_semaphore_t做线程同步。

    dispatch_semaphore_t的原理类似于semaphore,与其相关的方法主要是:

    dispatch_semaphore_t dispatch_semaphore_create(long value); long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

    dispatch_semaphore_create

    创建一个新的信号量,参数value代表信号量资源池的初始数量。

    value < 0, 返回NULL
    value = 0, 多线程在等待某个特定线程的结束。
    value > 0, 资源数量,可以由多个线程使用。

    dispatch_semaphore_wait

    等待资源释放。如果传入的dsema大于0,就继续向下执行,并将信号量减1;如果dsema等于0,阻塞当前线程等待资源被dispatch_semaphore_signal释放。如果等到了信号量,继续向下执行并将信号量减1,如果一直没有等到信号量,就等到timeout再继续执行。dsema不能传入NULL。

    timeout表示阻塞的时间长短,有两个常量:DISPATCH_TIME_NOW表示当前,DISPATCH_TIME_FOREVER表示永远。或者自己定义一个时间:

    dispatch_time_t  t = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000);
    

    dispatch_semaphore_signal

    释放一个资源。返回值为0表示没有线程等待这个信号量;返回值非0表示唤醒一个等待这个信号量的线程。如果线程有优先级,则按照优先级顺序唤醒线程,否则随机选择线程唤醒

    应用场景

    dispatch_semaphore_t的应用场景很多,这里以一个异步网络请求为例。
    在异步网络请求中,我们先发送网络请求,然后要等待网络结果返回再做其他事情。为了将这种异步请求改成同步的,我们可以使用dispatch_semaphore_t。

    static dispatch_semaphore_t match_sema; 
    -(void)asynNetWorkRequest
     { 
          /* ... 构造网络请求参数 ...
             [[IosNet sharedInstance] asyncCall:method forParam:reqData                forCallback:zuscallback forTimeout:timeoutValue]; 
          ... */ 
    
    //创建信号量,阻塞当前线程 
    static dispatch_once_t onceToken; 
      dispatch_once(&onceToken, ^{
      //信号量 :0
       match_sema = dispatch_semaphore_create(0); 
      }); 
      //信号量:0,当前线程会在这里阻塞,等待异步回调结束,signal+1
      dispatch_semaphore_wait(match_sema, DISPATCH_TIME_FOREVER); 
      //信号量为-1
    } 
    //请求成功 释放信号量,继续当前线程 
    -(void)onCallSuccess:(NSData *)rspData 
    { 
       if (match_sema) 
      { 
        dispatch_semaphore_signal(match_sema);
       } 
     }
     //请求失败 释放信号量,继续当前线程 
    -(void)onCallFail:(NSError *)errorInfo { if (match_sema)
     { 
        dispatch_semaphore_signal(match_sema);
     } }
    
    

    有一点需要注意,dispatch_semaphore_wait与 异步操作不能在同一个线程中,否则异步操作会被卡住,也就不会执行到dispatch_semaphore_signal

    以上例子相当于异步网络请求,同步执行


    加深理解

    首先,我理解的dispatch_semaphore有两个主要应用 :

    1. 保持线程同步
      **2. 为线程加锁 **

    先看下相关的3个方法:

    • dispatch_semaphore_t dispatch_semaphore_create(long value):方法接收一个long类型的参数, 返回一个dispatch_semaphore_t类型的信号量,值为传入的参数
    • long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout):接收一个信号和时间值,若信号的信号量为0,则会阻塞当前线程,直到信号量大于0或者经过输入的时间值;若信号量大于0,则会使信号量减1并返回,程序继续住下执行
    • long dispatch_semaphore_signal(dispatch_semaphore_t dsema):使信号量加1并返回

    介绍完了相关方法,下面开始介绍两种应用

    保持线程同步

    先看代码

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 
    __block int j = 0; dispatch_async(queue, ^{ j = 100; 
    dispatch_semaphore_signal(semaphore); }); 
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
    NSLog(@"finish j = %zd", j);
    

    结果输出 j = 100;

    如果注掉dispatch_semaphore_wait这一行,则 j = 0;
    注释: 由于是将block异步添加到一个并行队列里面,所以程序在主线
    程跃过block直接到dispatch_semaphore_wait这一行,因为semaphore
    信号量为0,时间值为DISPATCH_TIME_FOREVER,所以当前线程会一
    直阻塞,直到block在子线程执行到dispatch_semaphore_signal,使信
    号量+1,此时semaphore信号量为1了,所以程序继续往下执行。这就
    保证了线程间同步了。
    

    为线程加锁

    先看代码:

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); 
    for (int i = 0; i < 100; i++)
     { 
    dispatch_async(queue, ^{
     // 相当于加锁 
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
    NSLog(@"i = %zd semaphore = %@", i, semaphore); // 相当于解锁 
    dispatch_semaphore_signal(semaphore); 
    }); 
    }
    
    

    注释:当线程1执行到dispatch_semaphore_wait这一行时,semaphore的信号量为1,所以使信号量-1变为0,并且线程1继续往下执行;如果当在线程1NSLog这一行代码还没执行完的时候,又有线程2来访问,执行dispatch_semaphore_wait时由于此时信号量为0,且时间为DISPATCH_TIME_FOREVER,所以会一直阻塞线程2(此时线程2处于等待状态),直到线程1执行完NSLog并执行完dispatch_semaphore_signal使信号量为1后,线程2才能解除阻塞继续住下执行。以上可以保证同时只有一个线程执行NSLog这一行代码。

    相关文章

      网友评论

        本文标题:dispatch_semaphore_t异步任务同步执行和线程加

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