
以下是SDWebImage的主线程同步调用宏定义:
1 |
#define dispatch_main_sync_safe(block) |
这么写是由于dispatch_sync的文档中提到:
1 |
Calling this function and targeting the current queue results in deadlock. |
1 |
Do not call the dispatch_sync function from a task that is executing on the same queue that you pass to your function call. Doing so will deadlock the queue. If you need to dispatch to the current queue, do so asynchronously using the dispatch_async function. |
所以如果没有判断当前线程是不是主线程,而执行dispatch_sync,可能会造成死锁。死锁是什么样子的,可以通过如下代码体验一下。
1 |
NSLog(@"before"); |
为什么在当前线程进行同步调用会造成死锁,stackoverflow上有如下解释:
dispatch_sync does two things:
- queue a block
- blocks the current thread until the block has finished running
Given that the main thread is a serial queue (which means it uses only one thread), the following statement:
1 |
dispatch_sync(dispatch_get_main_queue(), ^(){/*...*/}); |
will cause the following events:
- dispatch_sync queues the block in the main queue.
- dispatch_sync blocks the thread of the main queue until the block finishes executing.
- dispatch_sync waits forever because the thread where the block is supposed to run is blocked.
The key to understanding this is that dispatch_sync does not execute blocks, it only queues them. Execution will happen on a future iteration of the run loop.
The following approach:
1 |
if (queueA == dispatch_get_current_queue()){ |
is perfectly fine, but be aware that it won’t protect you from complex scenarios involving a hierarchy of queues. In such case, the current queue may be different than a previously blocked queue where you are trying to send your block. Example:
1 |
dispatch_sync(queueA, ^{ |
For complex cases, read/write key-value data in the dispatch queue:
1 |
dispatch_queue_t workerQ = dispatch_queue_create("com.meh.sometask", NULL); |
Explanation:
- I create a workerQ queue that points to a funnelQ queue. In real code this is useful if you have several “worker” queues and you want to resume/suspend all at once (which is achieved by resuming/updating their target funnelQ queue).
- I may funnel my worker queues at any point in time, so to know if they are funneled or not, I tag funnelQ with the word “funnel”.
- Down the road I dispatch_sync something to workerQ, and for whatever reason I want to dispatch_sync to funnelQ, but avoiding a dispatch_sync to the current queue, so I check for the tag and act accordingly. Because the get walks up the hierarchy, the value won’t be found in workerQ but it will be found in funnelQ. This is a way of finding out if any queue in the hierarchy is the one where we stored the value. And therefore, to prevent a dispatch_sync to the current queue.
If you are wondering about the functions that read/write context data, there are three:
- dispatch_queue_set_specific: Write to a queue.
- dispatch_queue_get_specific: Read from a queue.
- dispatch_get_specific: Convenience function to read from the current queue.
The key is compared by pointer, and never dereferenced. The last parameter in the setter is a destructor to release the key.
If you are wondering about “pointing one queue to another”, it means exactly that. For example, I can point a queue A to the main queue, and it will cause all blocks in the queue A to run in the main queue (usually this is done for UI updates).




近期评论