dispatch_semaphore的一点个人测试

关于信号量 dispatch_semaphore 的一些个人思考

信号量,表示了一个数字。
每一个执行的线程,可以向这个信号量申请一个数字。
如果申请到了,总信号量-1。当前线程执行完毕,信号量+1。

相关场景1
当某个线程需要执行某个任务的时候,向信号量请求1个数字。
如果信号量剩余的数字大于0,那么请求就成功。
且当前线程,会将此信号量的数字-1。
当这个线程执行完毕之后,要将这个信号量的数字+1。

这是很多博客里,关于信号量的使用的“标准答案。

当然,那种放荡不羁的线程,压根就不想和信号量产生半毛钱关系。
自己想执行就执行,完全不在乎信号量是否存在。
但随着而来的,就可以产生数据的混乱。

信号量只是一种线程主动愿意被信号控制的手段。

下面几点,是对信号量的几种非常规的测试。并不代表这一定是正确的。


我非不要 dispatch_semaphore_wait & dispatch_semaphore_signal 一起使用。

获取到的信号量-1操作,完成之后,就不把信号量+1.

#pragma mark - 信号量,-1之后,不+1.
- (void)semaphoreDemo1 {
    // 1.创建一个信号为1的信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    // 2.当前 UI 线程获取这个信号量
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // 3.拿到了信号,执行方法
    NSLog(@"%@",@"我是 UI 主线程,我拿到了信号量。");

    // dispatch_semaphore_signal(semaphore); // 不把信号量+1
}

运行结果:

image.png

程序直接崩溃了。
但这个是在主线程。我换子线程试试?

在子线程,信号量-1,不+1

- (void)semaphoreDemo2 {
    // 1.创建一个信号为1的信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"%@",@"我是子线程,我拿到了1个信号");
        // dispatch_semaphore_signal(semaphore); // 不把信号量+1
    });

    NSLog(@"%@",@"主线程执行");
}

运行结果:

[图片上传失败...(image-a4bd20-1510308500036)]

发现仍然会崩溃。
崩溃信息是:

bug in client of libdispatch semaphore object deallocated while in use.

好像表明了,我在使用一个释放了的信号量对象。

OK,那我把信号量设置成全局的再试一次。

@implementation ViewController {
    dispatch_semaphore_t _semaphore;
}
- (void)semaphoreDemo2 {
    // 1.创建一个信号为1的信号量
    _semaphore = dispatch_semaphore_create(1);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"%@",@"我是子线程,我拿到了1个信号");
        // dispatch_semaphore_signal(_semaphore); // 不把信号量+1
    });

    NSLog(@"%@",@"主线程执行");
}

点击屏幕第一下。

运行结果:

[ViewController.m]  :[63]   [主线程执行]
[ViewController.m]  :[59]   [我是子线程,我拿到了1个信号]

程序运行正常。

然后,毫无意识了点击了屏幕第二下。

image.png

程序崩溃。

现在有:

  1. 一个全局的信号量属性。
  2. 初始化的时候,设置了信号量为1.
  3. 点击屏幕的时候,拿到一个信号量,此时的信号量为0.这个全局属性信号量的为0
  4. 第二次点击屏幕的时候,重复第一步。

但在第一步的时候报错了。
为什么一个新的信号量属性无法创建呢?

现在唯一的猜测:

难道是因为前一个属性的信号量 -1了,没有执行+1的操作?

然后上代码,使用完毕之后,把信号量+1.

- (void)semaphoreDemo2 {
    // 1.创建一个信号为1的信号量
    _semaphore = dispatch_semaphore_create(1);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"%@",@"我是子线程,我拿到了1个信号");
        // 使用完毕之后,把信号量+1.
        dispatch_semaphore_signal(_semaphore);
    });

    NSLog(@"%@",@"主线程执行");
}

继续点击屏幕两次。

[ViewController.m]  :[64]   [主线程执行]
[ViewController.m]  :[59]   [我是子线程,我拿到了1个信号]
[ViewController.m]  :[64]   [主线程执行]
[ViewController.m]  :[59]   [我是子线程,我拿到了1个信号]

运行变的正常了。
说明了:

信号量在使用的时候,-1 和 + 1操作,尽量要成对出现。否则程序可能会崩溃。
至于为什么要这样,我也不知道。

那么在主线程,信号量崩溃的原因,也是因为信号量没有按照使用 -1 + 1 吗?

- (void)semaphoreDemo1 {
    // 1.创建一个信号为1的信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    // 2.当前 UI 线程获取这个信号量
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // 3.拿到了信号,执行方法
    NSLog(@"%@",@"我是 UI 主线程,我拿到了信号量。");

    // 按照信号量的使用规则 -1 +1.不管在哪个线程,都应该这样。
    dispatch_semaphore_signal(semaphore);
}

运行结果:

[ViewController.m]  :[47]   [我是 UI 主线程,我拿到了信号量。]
[ViewController.m]  :[47]   [我是 UI 主线程,我拿到了信号量。]

发现一切正常。

所以,第一条结论:
使用信号量的时候,dispatch_semaphore_wait & dispatch_semaphore_signal 一定要成对出现。


都说如果 dispatch_semaphore_wait 如果请求的信号 == 0的话,当前线程会被阻塞。

试试看。

主线程,贱贱的拿走仅有的一个信号。
导致异步子线程在拿信号的时候就没有了。
如果说 dispatch_semaphore_wait 是等待的话,那么子线程 NSLog() 是无法输出的。

- (void)demo3 {
    _semaphore = dispatch_semaphore_create(1);
    // 主线程拿了信号了。
    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 子线程,拿不到这个信号,除非主线程 signal 一下。
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"%@",@"完了,我被卡死了。");
    });

    NSLog(@"%@",@"我就不把信号还原");
}

运行结果:

[ViewController.m]  :[81]   [我就不把信号还原]

测试结果:

的确如此。
dispatch_semaphore_wait 如果拿不到信号,会阻塞当前线程。

改进: 当主线程嘚瑟2秒,才还原信号。

- (void)demo3 {
    _semaphore = dispatch_semaphore_create(1);
    // 主线程拿了信号了。
    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 子线程,拿不到这个信号,除非主线程 signal 一下。
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"%@",@"完了,我被卡死了。");
        
         // 子线程也乖乖的把信号还原
        dispatch_semaphore_signal(_semaphore);
    });

    NSLog(@"%@",@"我就不把信号还原,让我嘚瑟2秒");

    sleep(2);

    NSLog(@"还是把信号还原吧。")

    dispatch_semaphore_signal(_semaphore);
}

运行结果:

[ViewController.m]  :[81]   [我就不把信号还原,让我嘚瑟2秒]
[ViewController.m]  :[85]   [还是把信号还原吧。]
[ViewController.m]  :[78]   [完了,我被卡死了。]

第二个结论:
dispatch_semaphore_wait会尝试从信号源获取一个信号,如果获取到了,当前线程继续执行。
如果获取不到,就一直等待,直到别的线程释放了一个信号为止。


使用 dispatch_semaphore_create 创建信号源数量是固定的吗?

使用 dispatch_semaphore_create。需要传入一个 long 的数值,表示信号源个数。
使用的时候,信号源 -1 ,使用完毕信号源 + 1.

但从来没有看到说,信号源的数量是一开始就指定和是只读的吗?
我能修改这个数量吗?

试试看。

  1. 创建一个信号为1 的信号源
  2. 连续3次 signal ,信号源变成4.(如果可以的话)
  3. 主线程拿走一个信号源,还剩下3个。
  4. 开启3个子线程,每个人都分别走剩下的3个信号源。
  5. 如果子线程代码输入的线程编号是3个不一样的数字,那么结论就是成立的。(什么结论?)
- (void)demo4 {
    // 默认信号源是1.
    _semaphore = dispatch_semaphore_create(1);

    // 信号源+1
    dispatch_semaphore_signal(_semaphore);
    // 信号源+1
    dispatch_semaphore_signal(_semaphore);
    // 信号源+1
    dispatch_semaphore_signal(_semaphore);
    // 现在有4个信号源了。至少大于1个。

    // 证明一下。
    // 主线程那走1个,还剩下3个。

    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"%@",@"主线程执行完毕。");
    // 先不还原
    for (NSInteger i = 0; i < 3; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // 子线程拿信号源
            dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
            // 如果这里能打印出3个不同的线程,就说明一开始传入的那个 long 参数,并不是不可以修改的。
            NSLog(@"%@",[NSThread currentThread]);

        });
    }
}

运行结果:

[ViewController.m]  :[114]  [主线程执行完毕。]
[ViewController.m]  :[121]  [<NSThread: 0x610000077f40>{number = 4, name = (null)}]
[ViewController.m]  :[121]  [<NSThread: 0x600000078b00>{number = 3, name = (null)}]
[ViewController.m]  :[121]  [<NSThread: 0x618000077280>{number = 5, name = (null)}]

第三条结论
使用 dispatch_semaphore_t dispatch_semaphore_create(long value) 函数,传递进去的 long 并不是不可以修改的。
而是非常灵活的可以使用 disaptch_semaphore_signal 来随意的修改信号量的总量。


最后总结:

  1. 信号量是一个公用资源。
  2. 相关的不相关的线程都可以从同一个信号源申请信号。
  3. 如果信号 > 0 ,那么就应该手动的把信号 - 1 dispatch_semaphore_wait
  4. 使用完毕,则应该必须手动把信号 + 1 dispatch_semaphore_signal
  5. 如果不做这些应该做的事情,就会被无情的崩溃。
  6. 信号量一开始传递出进去的 long 参数,并不代码了信号量就只有那么多。
  7. 我们可以随意的使用 dispatch_semaphore_signal 增加信号。
  8. 为什么不测试 dispatch_semaphore_wait - 信号?因为,这个方法如果拿不到信号,后面的代码就不执行了。第二点已经证明了这个观点。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容