关于块捕获值和 dispatch_async 的有趣事情

Interesting thing about block captured value and dispatch_async

今天,当我用 dispatch_async 测试一些代码时,我发现了一件有趣的事情,当我 运行 一些这样的代码时:

static int temp = 1;
    dispatch_queue_t defaultBackgroundQueue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
dispatch_async(defaultBackgroundQueue, ^{
        NSLog(@"blk 0 :%d", temp);
    });
    dispatch_async(defaultBackgroundQueue, ^{
        NSLog(@"blk 1 :%d", temp++);
    });
    dispatch_async(defaultBackgroundQueue, ^{
        NSLog(@"blk 2 :%d", temp);
    });

猜猜日志是什么?很有意思,像这样:

2016-02-29 21:39:40.700 GCDDemo[46594:3826498] blk 2 :2
2016-02-29 21:39:40.700 GCDDemo[46594:3826496] blk 1 :1
2016-02-29 21:39:40.696 GCDDemo[46594:3826495] blk 0 :1

I have tried many times, even the third block finished firstly, the value of temp is 2, before the ++ executed. Shouldn't it be 1 ?

我使用clang将代码转换成C++后,没什么特别的,关键代码:

static void __main_block_func_1(struct __main_block_impl_1 *__cself) {
int *temp = __cself->temp; // bound by copy
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_s6_v33rqm893pddvmp9w2hyfhtc0000gn_T_main_d902a1_mi_1, (*temp)++);
    } 

static int temp = 1;
dispatch_queue_t defaultBackgroundQueue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
dispatch_async(defaultBackgroundQueue, ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &temp)));
dispatch_async(defaultBackgroundQueue, ((void (*)())&__main_block_impl_1((void *)__main_block_func_1, &__main_block_desc_1_DATA, &temp)));
dispatch_async(defaultBackgroundQueue, ((void (*)())&__main_block_impl_2((void *)__main_block_func_2, &__main_block_desc_2_DATA, &temp)));

你可以自己试一试,我在我的 Mac book pro 上测试过,OS X 10.11, 有人知道这件事吗? 谢谢,

defaultBackgroundQueue 是并发的,所以你不应该依赖执行顺序。很可能 temp++ 已经执行,但来自另一个块的 NSLog 没有。如果您想要不同的行为,请使用串行调度队列。

这里发生的事情如下:

异步线程确实以不同于您在 print/log 语句中看到的顺序完成,这意味着 NSLog 语句执行 after 任何同时已经设法对(共享)基础值 temp 进行计算的其他线程(这可能是由于 NSLog 语句需要(相当)更多的时间才能完成来自其他线程的简单计算完成)。

Boris 也有一个正确的观点,显然,线程确实并发执行,我相信您已经意识到这一点,但它并不总是一开始就那么明显。

我把NSLog改成printf后,加了一个sleep:

dispatch_async(defaultBackgroundQueue, ^{
        sleep(1);
        printf("blk 0:%d\n", temp);
    });
    dispatch_async(defaultBackgroundQueue, ^{
        sleep(1);
        printf("blk 1:%d\n", temp++);
    });
    dispatch_async(defaultBackgroundQueue, ^{
        sleep(1);
        printf("blk 2:%d\n", temp);
    });
    dispatch_async(defaultBackgroundQueue, ^{
        sleep(1);
        printf("blk 3:%d\n", temp);
    });

结果改变了:

blk 1:1
blk 0:2
blk 2:2
blk 3:2

正如@the_critic所说,NSLog并不是实际结果。

谢谢,