__block 变量赋值是线程安全的,可以在块后立即读取吗?
Is a __block variable assignment thread-safe to read immediately after the block?
__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()
而不是信号量完成的,那将是安全的,因为它保证了与 "block".
中赋值的先行关系
简短的回答是肯定的。
信号量锁本质上是强制当前正在运行的线程停止执行,直到它收到足够的解锁信号才能继续。
在允许信号量继续执行之前,您定义的变量在其他线程上被修改,因此您的赋值应该是安全发生的。
看来dispatch_semaphore_wait
也是一个内存屏障,所以可以安全读取值。
严格来说,此代码将阻塞在执行线程(可能是主线程)上,直到发出信号量锁信号为止。所以 - 简短的回答,是的,它应该可以工作,但这不是最佳实践,因为它会阻塞主线程。
更长的答案:
是的,信号量将确保 __block 捕获的存储在被填充之前不会被访问。但是,调用线程将被等待直到块完成。这并不理想 - 确保 Activity 指标旋转等正常 UI 任务不会发生。
最佳做法是在主对象完成后让块向主对象发出信号(可能使用 dispatch_async 调用主队列),并且仅在此之后访问它。考虑到如果您的会话任务失败(例如,由于网络连接失败),调用线程可能会阻塞,直到调用完成处理程序并出现超时错误,这一点尤其正确。这对用户来说就像应用程序已冻结一样,他们对此无能为力,只能终止应用程序。
有关使用块的更多信息,请参阅:
特别是数据会话任务的最佳实践:
__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()
而不是信号量完成的,那将是安全的,因为它保证了与 "block".
简短的回答是肯定的。
信号量锁本质上是强制当前正在运行的线程停止执行,直到它收到足够的解锁信号才能继续。
在允许信号量继续执行之前,您定义的变量在其他线程上被修改,因此您的赋值应该是安全发生的。
看来dispatch_semaphore_wait
也是一个内存屏障,所以可以安全读取值。
严格来说,此代码将阻塞在执行线程(可能是主线程)上,直到发出信号量锁信号为止。所以 - 简短的回答,是的,它应该可以工作,但这不是最佳实践,因为它会阻塞主线程。
更长的答案:
是的,信号量将确保 __block 捕获的存储在被填充之前不会被访问。但是,调用线程将被等待直到块完成。这并不理想 - 确保 Activity 指标旋转等正常 UI 任务不会发生。
最佳做法是在主对象完成后让块向主对象发出信号(可能使用 dispatch_async 调用主队列),并且仅在此之后访问它。考虑到如果您的会话任务失败(例如,由于网络连接失败),调用线程可能会阻塞,直到调用完成处理程序并出现超时错误,这一点尤其正确。这对用户来说就像应用程序已冻结一样,他们对此无能为力,只能终止应用程序。
有关使用块的更多信息,请参阅:
特别是数据会话任务的最佳实践: