是一个__block变量赋值线程安全的块后立即读取?

时间:2015-05-15 13:58:48

标签: ios objective-c synchronization thread-safety objective-c-blocks

__block NSHTTPURLResponse *httpResponse;

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (!error) 
        httpResponse = (NSHTTPURLResponse *)response;
    }

    dispatch_semaphore_signal(semaphore);
}];
[task resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

在此之后阅读httpResponse是否安全?信号量等待块执行竞争。如果没有错误,是否会立即看到作业,还是必须在块外同步或创建内存屏障?

等待信号量是否隐式执行某些同步,这使得__block变量可以立即读取。如果用Java中的Thread.join()而不是信号量来完成,那么它将是安全的,因为它保证了与"块"中的赋值之间发生的关系。

3 个答案:

答案 0 :(得分:0)

简短的回答是肯定的。

信号量锁实际上会强制当前运行的线程停止执行,直到它收到足够的解锁信号才能继续。

在允许信号量继续执行之前,在其他某个线程上修改了您定义的变量,因此您的分配应该安全地进行。

答案 1 :(得分:0)

似乎dispatch_semaphore_wait也是一个内存屏障,因此可以安全地读取该值。

答案 2 :(得分:0)

严格地说,此代码将阻止执行线程(可能是主线程),直到发出信号量锁信号。所以 - 简短的回答,是的它应该有效,但这不是最佳实践,因为它阻止了主线程。

更长的答案:

是的,信号量将确保__​​block捕获的存储在被填充之前不被访问。但是,调用线程将被等待阻塞直到块完成。这不是理想的 - 正常的UI任务,如确保活动指标旋转不会发生。

最佳做法是在块完成后让块信号成为主要对象(可能使用对主队列的dispatch_async调用),之后仅访问它。特别是如果您的会话任务失败(例如,来自网络连接),则调用线程可能会阻塞,直到调用完成处理程序并出现超时错误。这对应用程序来说似乎已经冻结,并且他们无法对此做任何事情,但会杀死该应用程序。

有关使用块的更多信息,请参阅:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html

特别针对数据会话任务的最佳实践:

http://www.raywenderlich.com/51127/nsurlsession-tutorial