信号量

作者: 堂吉诃德灬 | 来源:发表于2017-03-01 16:53 被阅读64次

    1.信号量使用的情况

    信号量一般用来进行临界访问或者互斥访问的。临界资源可以理解为共享资源,这个共享资源每次每次只允许一个进程进行访问,当一个线程进入后,其他线程是不允许访问这一块资源的,只能等这一线程访问完后才能进入访问。

    2.P V操作

    P V操作主要是为了对信号量进行申请或者释放。P操作表示申请一个资源,V操作表示释放一个资源。信号量由一个值和一个指针组成,指针指向等待该信号量的进程。信号量的值表示相应资源的使用情况。当信号量S>=0时,S表示可用资源的数量。执行P操作意味着请求资源,此时S减一,当S<0时,表示没有可用的资源,此时S的绝对值表示当前等待该资源的进程数。这些进程必须等资源被释放后才能继续运行。而执行V操作意味着释放一个资源,S加1。

    3.GCD中的信号量

    GCD中有三个函数是semaphore的操作:
    Dispatch_semaphore_create //创建一个信号量
    Dispatch_semaphore_signal //发送一个信号,让信号量增加一
    Dispatch_semaphore_wait //等待信号,如果信号总量大于0,则减掉一个信号量

    4.GCD中信号量的使用

    (1)首先看一段并发的代码

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第一组现在是%d",i);
            }
        });
    
        NSLog(@"现在我执行了");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第二组现在是%d",i);
            }
        });
    
    

    输出结果:

    2017-03-01 15:01:43.714 信号量[6437:260451] 第一组现在是1
    2017-03-01 15:01:43.714 信号量[6437:260466] 第二组现在是0
    2017-03-01 15:01:43.714 信号量[6437:260466] 第二组现在是1
    2017-03-01 15:01:43.714 信号量[6437:260451] 第一组现在是2
    2017-03-01 15:01:43.714 信号量[6437:260466] 第二组现在是2
    2017-03-01 15:01:43.714 信号量[6437:260451] 第一组现在是3
    2017-03-01 15:01:43.714 信号量[6437:260466] 第二组现在是3
    2017-03-01 15:01:43.715 信号量[6437:260451] 第一组现在是4
    2017-03-01 15:01:43.715 信号量[6437:260466] 第二组现在是4
    2017-03-01 15:01:43.715 信号量[6437:260451] 第一组现在是5
    2017-03-01 15:01:43.715 信号量[6437:260466] 第二组现在是5
    2017-03-01 15:01:43.715 信号量[6437:260451] 第一组现在是6
    2017-03-01 15:01:43.715 信号量[6437:260466] 第二组现在是6
    2017-03-01 15:01:43.715 信号量[6437:260451] 第一组现在是7
    2017-03-01 15:01:43.715 信号量[6437:260466] 第二组现在是7
    2017-03-01 15:01:43.715 信号量[6437:260451] 第一组现在是8
    2017-03-01 15:01:43.715 信号量[6437:260466] 第二组现在是8
    2017-03-01 15:01:43.715 信号量[6437:260451] 第一组现在是9
    2017-03-01 15:01:43.716 信号量[6437:260466] 第二组现在是9
    
    

    从上面的输出结果来看这一段代码是并发执行的,第一组和第二组交替执行。
    关于进程、线程的概念以及多线程可以参考我另一篇文章
    iOS多线程的简介及使用(http://www.jianshu.com/p/59da6f924e95

    (2)那么现在我想上面的代码第一组执行完了再执行第二段该怎么办呢,下面我们加入信号量来试一下

        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15.0 * NSEC_PER_SEC));
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第一组现在是%d",i);
            }
           dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, time);
        NSLog(@"现在我执行了");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第二组现在是%d",i);
            }
    });
    
    

    输出结果:

    2017-03-01 15:08:37.848 信号量[6577:266811] 第一组现在是0
    2017-03-01 15:08:37.848 信号量[6577:266811] 第一组现在是1
    2017-03-01 15:08:37.848 信号量[6577:266811] 第一组现在是2
    2017-03-01 15:08:37.848 信号量[6577:266811] 第一组现在是3
    2017-03-01 15:08:37.849 信号量[6577:266811] 第一组现在是4
    2017-03-01 15:08:37.849 信号量[6577:266811] 第一组现在是5
    2017-03-01 15:08:37.849 信号量[6577:266811] 第一组现在是6
    2017-03-01 15:08:37.849 信号量[6577:266811] 第一组现在是7
    2017-03-01 15:08:37.849 信号量[6577:266811] 第一组现在是8
    2017-03-01 15:08:37.850 信号量[6577:266811] 第一组现在是9
    2017-03-01 15:08:37.850 信号量[6577:266778] 现在我执行了
    2017-03-01 15:08:37.850 信号量[6577:266811] 第二组现在是0
    2017-03-01 15:08:37.850 信号量[6577:266811] 第二组现在是1
    2017-03-01 15:08:37.850 信号量[6577:266811] 第二组现在是2
    2017-03-01 15:08:37.851 信号量[6577:266811] 第二组现在是3
    2017-03-01 15:08:37.851 信号量[6577:266811] 第二组现在是4
    2017-03-01 15:08:37.851 信号量[6577:266811] 第二组现在是5
    2017-03-01 15:08:37.851 信号量[6577:266811] 第二组现在是6
    2017-03-01 15:08:37.851 信号量[6577:266811] 第二组现在是7
    2017-03-01 15:08:37.852 信号量[6577:266811] 第二组现在是8
    2017-03-01 15:08:37.852 信号量[6577:266811] 第二组现在是9
    
    

    从上面的结果来看我们的代码达到了我们预期的效果,那么我们来分析一下上面的代码我们到底做了什么事情:
    首先我们创建了一个信号量,这个信号量的初始值为0
    然后我们设置了一个超时时间,在第一组的循环结束时我们释放了信号量,即进行了V操作,然后在第二组循环代码前面我们进行了P操作,请求了信号量我们的代码相比上面的不同点之处是多了这4句代码

    代码的不同之处分析完了,那么我们再来分析一下为什么会出现这种结果:
    信号量的初始值为0,然后我们在一个并行队列里面异步执行一个for 循环,因为是异步,所以我们可以直接往下面的代码走,这是我们进行了P操作,信号量要减一,因为信号量此时已经为0,我们可以理解为没有多余资源,此时要堵塞线程等待资源,然后等第一组的for循环执行完毕之后,信号量被加1,此时有了资源,于是代码继续往下执行,进入第二组for循环。

    (3)看到这里你是不是已经认为自己已经掌握了信号量,觉得自己能独步武林,笑傲江湖,年轻人 too young too simple我们再来看一段代码:

        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15.0 * NSEC_PER_SEC));
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第一组现在是%d",i);
                 dispatch_semaphore_signal(semaphore);
            }
        });
        dispatch_semaphore_wait(semaphore, time);
        NSLog(@"现在我执行了");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第二组现在是%d",i);
            }
        });
    
    

    输出结果:

    2017-03-01 15:34:02.068 信号量[7015:284862] 第一组现在是0
    2017-03-01 15:34:02.069 信号量[7015:284862] 第一组现在是1
    2017-03-01 15:34:02.069 信号量[7015:284816] 现在我执行了
    2017-03-01 15:34:02.069 信号量[7015:284862] 第一组现在是2
    2017-03-01 15:34:02.069 信号量[7015:284863] 第二组现在是0
    2017-03-01 15:34:02.069 信号量[7015:284862] 第一组现在是3
    2017-03-01 15:34:02.069 信号量[7015:284863] 第二组现在是1
    2017-03-01 15:34:02.069 信号量[7015:284862] 第一组现在是4
    2017-03-01 15:34:02.069 信号量[7015:284863] 第二组现在是2
    2017-03-01 15:34:02.069 信号量[7015:284862] 第一组现在是5
    2017-03-01 15:34:02.069 信号量[7015:284863] 第二组现在是3
    2017-03-01 15:34:02.070 信号量[7015:284862] 第一组现在是6
    2017-03-01 15:34:02.070 信号量[7015:284863] 第二组现在是4
    2017-03-01 15:34:02.070 信号量[7015:284862] 第一组现在是7
    2017-03-01 15:34:02.070 信号量[7015:284863] 第二组现在是5
    2017-03-01 15:34:02.070 信号量[7015:284862] 第一组现在是8
    2017-03-01 15:34:02.070 信号量[7015:284863] 第二组现在是6
    2017-03-01 15:34:02.070 信号量[7015:284862] 第一组现在是9
    2017-03-01 15:34:02.070 信号量[7015:284863] 第二组现在是7
    2017-03-01 15:34:02.071 信号量[7015:284863] 第二组现在是8
    2017-03-01 15:34:02.071 信号量[7015:284863] 第二组现在是9
    
    

    看到这个输出结果,有些人可能一脸懵逼,为什么,我觉得这代码一样啊,我们再来仔细看看这段代码与上面的有什么不同,仔细看我们会发现唯一的不同点在于信号量的发送时间不同,即进行V操作的时间不一样,在上面的代码里面我们是在for循环结束之后才进行发送信号量的操作,但是在这里我们是每进行一次for循环我们就发送一次信号量,这样其实信号量一直在增加,于是第二个for 循环就不会被阻塞,就出现了我们看到的结果(其实这一块第一组的0一定是先于现在我执行了这句话执行的,童鞋们可以多试一试看看是不是这种情况)

    (4)我们再来看一种情况

        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15.0 * NSEC_PER_SEC));
         dispatch_semaphore_wait(semaphore, time);
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第一组现在是%d",i);
            }
                      dispatch_semaphore_signal(semaphore);
        });
       
        NSLog(@"现在我执行了");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 10; i ++) {
                NSLog(@"第二组现在是%d",i);
            }
        });
    
    

    输出结果:

    2017-03-01 16:10:43.640 信号量[7549:307322] 第一组现在是0
    2017-03-01 16:10:43.640 信号量[7549:307270] 现在我执行了
    2017-03-01 16:10:43.640 信号量[7549:307322] 第一组现在是1
    2017-03-01 16:10:43.640 信号量[7549:307743] 第二组现在是0
    2017-03-01 16:10:43.640 信号量[7549:307322] 第一组现在是2
    2017-03-01 16:10:43.641 信号量[7549:307743] 第二组现在是1
    2017-03-01 16:10:43.641 信号量[7549:307322] 第一组现在是3
    2017-03-01 16:10:43.641 信号量[7549:307743] 第二组现在是2
    2017-03-01 16:10:43.641 信号量[7549:307322] 第一组现在是4
    2017-03-01 16:10:43.641 信号量[7549:307743] 第二组现在是3
    2017-03-01 16:10:43.641 信号量[7549:307322] 第一组现在是5
    2017-03-01 16:10:43.642 信号量[7549:307743] 第二组现在是4
    2017-03-01 16:10:43.642 信号量[7549:307322] 第一组现在是6
    2017-03-01 16:10:43.642 信号量[7549:307743] 第二组现在是5
    2017-03-01 16:10:43.642 信号量[7549:307322] 第一组现在是7
    2017-03-01 16:10:43.642 信号量[7549:307743] 第二组现在是6
    2017-03-01 16:10:43.642 信号量[7549:307322] 第一组现在是8
    2017-03-01 16:10:43.643 信号量[7549:307743] 第二组现在是7
    2017-03-01 16:10:43.643 信号量[7549:307322] 第一组现在是9
    2017-03-01 16:10:43.643 信号量[7549:307743] 第二组现在是8
    2017-03-01 16:10:43.643 信号量[7549:307743] 第二组现在是9
    

    从输出结果上来看好像两个for循环是并发执行的,但是童鞋们可以跑一下这段代码,你会发现程序运行起来之后会等一段时间才会有输出结果,这是为什么呢,因为刚开始信号量是0,然后我们设置了一个超时时间,当进行P操作对信号量减一的时候会堵塞,因为没有可用资源,等到超时时间过去程序才会继续往下走。这就导致了我们前期有一段时间一直没有输出结果,其实是因为线程被堵塞了。

    总结:对信号量进行P V操作一定要考虑清楚应该在什么时候进行,不然可能会达不到我们预期的效果,同时当信号量为0的时候代码是可以继续往下执行的,只要你不进行P操作(有些博客上说当信号量为0 的时候就阻塞了,但我测试为0的时候代码是可以往下执行的,不知道是不是我理解的有偏差),当被阻塞的时候,过了超时时间代码也可以继续往下执行。以上就是我对信号量的初步理解,有什么不对的还要大家多指教,如果有什么好的关于信号量的博客大家也可以推荐一下,共同研究。

    相关文章

      网友评论

        本文标题:信号量

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