信号量

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的时候代码是可以往下执行的,不知道是不是我理解的有偏差),当被阻塞的时候,过了超时时间代码也可以继续往下执行。以上就是我对信号量的初步理解,有什么不对的还要大家多指教,如果有什么好的关于信号量的博客大家也可以推荐一下,共同研究。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容