Samuel
Samuel

Reputation: 127

Interesting thing about block captured value and dispatch_async

Today when I test some code with dispatch_async, I found a interesting thing, when I run some code like this:

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);
    });

Guess what's the log is? It's very interesting, like this:

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 ?

After I converted the code into C++ using clang, nothing special, key code:

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)));

You can give it a try yourself, I tested it on my Mac book pro, OS X 10.11, Any one know something about this ? Thanks,

Upvotes: 1

Views: 90

Answers (3)

Samuel
Samuel

Reputation: 127

After I change the NSLog to printf, and add a 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);
    });

the result changed:

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

so as @the_critic said, NSLog didn't mean the actually result.

Thanks,

Upvotes: 0

the_critic
the_critic

Reputation: 12820

What happens here is the following:

The asynchronous threads do complete in a different order than what you see in the the print/log statements meaning that the NSLog statement executes after any of the other threads which meanwhile already did manage to do a computation on the (shared) underlying value temp (this is probably due to the fact that the NSLog statement takes (considerably) more time to finish before any of the simple calculations from the other threads finish).

Boris also has a valid point, obviously, the threads do execute concurrently, I'm sure you are aware of that, but it's not always as obvious in the first place.

Upvotes: 1

Borys Verebskyi
Borys Verebskyi

Reputation: 4268

defaultBackgroundQueue is concurrent, so you shouldn't relay on execution order. It's quite possible, that temp++ is already executed, but NSLog from another block is not. If you want different behavior, use serial dispatch queue.

Upvotes: 0

Related Questions