ios - dispatch_queue_set_specific 与获取当前队列

标签 ios objective-c-blocks grand-central-dispatch deadlock

我试图弄清楚这两者之间的区别和用法:

static void *myFirstQueue = "firstThread";

dispatch_queue_t firstQueue = dispatch_queue_create("com.year.new.happy", DISPATCH_QUEUE_CONCURRENT);

dispatch_queue_set_specific(firstQueue, myFirstQueue, (void*) myFirstQueue, NULL);

问题 #1

这有什么区别:
dispatch_sync(firstQueue, ^{

    if(dispatch_get_specific(myFirstQueue))
    {
        //do something here
    }
});

以及以下内容:
dispatch_sync(firstQueue, ^{

    if(firstQueue == dispatch_get_current_queue())
    {
       //do something here
    }
});

?

问题 #2:

而不是使用上面的(void*) myFirstQueue
dispatch_queue_set_specific(firstQueue, myFirstQueue, (void*) myFirstQueue, NULL);

我们可以使用 static int * myFirstQueue = 0;反而 ?

我的推理基于以下事实:
dispatch_once_t也是 0 (这里有什么相关性吗?顺便说一句,我仍然不太明白为什么 dispatch_once_t 必须初始化为 0,尽管我已经阅读了关于 SO 的问题)。

问题 #3

你能在这里举一个 GCD 死锁的例子吗?

问题 #4

这可能有点过分了。无论如何,我会问,以防有人碰巧知道这一点。如果没有,可以不回答这部分。

这个我没试过,因为我真的不知道怎么做。但我的概念是这样的:

无论如何,我们是否可以在某个队列中“放置一个句柄”,使我们仍然能够保留一个句柄,从而能够检测到队列分离后何时发生死锁;当存在时,并且由于我们获得了之前设置的队列句柄,我们可以以某种方式做一些事情来解锁死锁吗?

同样,如果这个回答太多,或者我的推理在此处完全不可撤销/关闭(在 问题 #4 中),请随时不要回答这部分。

新年快乐。

@san.t

static void *myFirstQueue = 0;
我们这样做:
dispatch_queue_set_specific(firstQueue, &myFirstQueue, &myFirstQueue, NULL);

完全可以理解。

但如果我们这样做:
static void *myFirstQueue = 1; 
//or any other number other than 0, it would be OK to revert back to the following?
dispatch_queue_set_specific(firstQueue, myFirstQueue, (void*) myFirstQueue, NULL);

关于 dispatch_once_t :

您能否详细说明一下:

为什么一定要dispatch_once_t首先是 0 ,以及它如何以及为什么需要在后期充当 bool 值?这是否与内存/安全性或先前的内存地址被其他不为 0 的对象( nil )占用的事实有关?

至于问题#3:

抱歉,我可能不太清楚:我并不是说我遇到了僵局。我的意思是是否有人可以向我展示导致死锁的 GCD 代码场景。

最后:

希望你能回答问题#4。如果没有,如前所述,没关系。

最佳答案

首先,我真的不认为您打算使该队列并发。 dispatch_sync()连接到并发队列并不能真正完成任何事情(并发队列不保证在其上运行的 block 之间的顺序)。所以,这个答案的其余部分假设你打算在那里有一个串行队列。另外,我将笼统地回答这个问题,而不是您的具体问题;希望没关系:)

使用 dispatch_get_current_queue() 有两个基本问题这边走。一个非常广泛的可以概括为“递归锁定是一个坏主意”,另一个可以概括为“您可以并且经常会有多个当前队列”的调度特定的。

问题 #1:递归锁定是个坏主意

私有(private)串行队列的通常用途是 保护代码的不变量 (“不变”是“必须是真实的东西”)。例如,如果您使用队列来保护对属性的访问以使其是线程安全的,则不变量是“此属性没有无效值”(例如:如果属性是结构,则一半如果同时从两个线程设置结构,则结构可以有一个新值,一半可以有旧值。串行队列强制一个线程或另一个线程在另一个线程开始之前完成整个结构的设置)。

我们可以推断,为了使这一点有意义,当开始执行串行队列上的 block 时,不变量必须保持不变(否则,它显然没有受到保护)。一旦 block 开始执行,它就可以打破不变量(例如,设置属性)而不必担心弄乱任何其他线程,只要不变量在它返回时再次保持(在这个例子中,属性必须完全放)。

总结只是为了确保您仍在关注:在串行队列上每个 block 的开始和结束处,队列所保护的不变量必须保持。在每个 block 的中间,它可能会被打破。

如果在 block 内部,您调用了试图使用受队列保护的东西的东西,那么您已经将这个简单的规则更改为更复杂的规则:而不是“在每个 block 的开头和结尾”它是“ 在开始、结束以及该 block 调用自身外部的任何点 "。换句话说,您现在必须检查 ,而不是在 block 级别考虑线程安全。每一行 每个 block 的。

这和 dispatch_get_current_queue() 有什么关系? ?使用 dispatch_get_current_queue() 的唯一理由这里是检查“我们已经在这个队列中了吗?”,如果您已经在当前队列中,那么您已经处于上述可怕的情况!所以不要那样做。使用私有(private)队列来保护事物,不要从它们内部调用其他代码。您应该已经知道“我在这个队列中吗?”的答案。它应该是“不”。

这是最大的原因dispatch_get_current_queue()已弃用:阻止人们尝试用它来模拟递归锁定(我上面已经描述过)。

问题 #2:您可以拥有多个当前队列!

考虑这段代码:

dispatch_async(queueA, ^{
    dispatch_sync(queueB, ^{
        //what is the current queue here?
    });
});

显然 queueB 是当前的,但我们还在 queueA 上! dispatch_sync导致 queueA 上的工作等待 queueB 上的工作完成,因此它们实际上都是“当前的”。

这意味着这段代码会死锁:
dispatch_async(queueA, ^{
    dispatch_sync(queueB, ^{
        dispatch_sync(queueA, ^{});
    });
});

您还可以使用目标队列来拥有多个当前队列:
dispatch_set_target_queue(queueB, queueA);
dispatch_sync(queueB, ^{
    dispatch_sync(queueA, ^{ /* deadlock! */ });
});

这里真正需要的是一个假设的“dispatch_queue_is_synchronous_with_queue(queueA, queueB)”,但因为这仅对实现递归锁定有用,而且我已经描述了这是一个坏主意……不太可能添加。

请注意,如果您只使用 dispatch_async() ,那么你就不会出现死锁。可悲的是,您根本无法免受比赛条件的影响。

关于ios - dispatch_queue_set_specific 与获取当前队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20860997/

相关文章:

iphone - 在启动前添加屏幕

ios - NSLocalizedStringFromTable 不起作用

objective-c - 带有完成 block 的自定义 xml 解析器类

具有重试功能的 NSURLConnection sendAsynchronousRequest

ios - 为什么 UITableViewCell 首先显示以前的值,然后才更新自身?

objective-c - 将旧的 Objective-C 代码转换为 ARC(自动引用计数)的问题

ios - 如何将类对象转换为 JSON 以供请求?

iphone - 使用 block 将NSDictionary转换为字符串?

objective-c-blocks - 如何在 SpriteKit 中使用 Swift "enumerateChildNodesWithName"?

grand-central-dispatch - dispatch_semaphore_create 的左值参数?