gpt4 book ai didi

objective-c - 为什么自定义并发队列上的 dispatch_sync 会死锁

转载 作者:技术小花猫 更新时间:2023-10-29 10:39:33 25 4
gpt4 key购买 nike

在自定义并发 dispatch_queue 上使用 dispatch_sync 时,我在我的应用程序中看到间歇性死锁。我使用的方法类似于 Mike Ash's blog 中描述的方法支持并发读取访问,但在 NSMutableDictionary 上支持线程安全突变,作为当前事件网络 RPC 请求的缓存。我的项目使用 ARC。

我创建队列:

dispatch_queue_t activeRequestsQueue = dispatch_queue_create("my.queue.name",
DISPATCH_QUEUE_CONCURRENT);

和可变字典
NSMutableDictionary *activeRequests = [[NSMutable dictionary alloc] init];

我从队列中读取元素是这样的:
- (id)activeRequestForRpc: (RpcRequest *)rpc
{
assert(![NSThread isMainThread]);
NSString * key = [rpc getKey];
__block id obj = nil;
dispatch_sync(activeRequestsQueue, ^{
obj = [activeRequests objectForKey: key];
});
return obj;
}

我从缓存中添加和删除 rpcs
- (void)addActiveRequest: (RpcRequest *)rpc
{
NSString * key = [rpc getKey];
dispatch_barrier_async(activeRequestsQueue, ^{
[activeRequests setObject: rpc forKey: key];
});
}

- (void)removeActiveRequest: (RpcRequest *)rpc
{
NSString * key = [rpc getKey];
dispatch_barrier_async(activeRequestsQueue, ^{
[activeRequests removeObjectForKey:key];
});
}

当我一次发出大量网络请求时,我在对 activeRequestForRpc 的调用中看到死锁,这让我相信障碍块之一(添加或删除)没有完成执行。我总是从后台线程调用 activeRequestForRpc,并且应用程序 UI 不会卡住,所以我认为它不必阻塞主线程,但我添加了 assert 语句以防万一。关于这种僵局如何发生的任何想法?

更新:添加调用这些方法的代码

我正在使用 AFNetworking 来发出网络请求,并且我有一个 NSOperationQueue,我正在调度“检查缓存并可能从网络中获取资源”逻辑。我将该操作称为 CheckCacheAndFetchFromNetworkOp。在该操作中,我调用 AFHTTPClient 的自定义子类以发出 RPC 请求。
// this is called from inside an NSOperation executing on an NSOperationQueue.
- (void) enqueueOperation: (MY_AFHTTPRequestOperation *) op {
NSError *error = nil;
if ([self activeRequestForRpc:op.netRequest.rpcRequest]) {
error = [NSError errorWithDomain:kHttpRpcErrorDomain code:HttpRpcErrorDuplicate userInfo:nil];
}
// set the error on the op and cancels it so dependent ops can continue.
[op setHttpRpcError:error];

// Maybe enqueue the op
if (!error) {
[self addActiveRequest:op.netRequest.rpcRequest];
[self enqueueHTTPRequestOperation:op];
}
}

MY_AFHTTRequestOperation 是由 AFHTTPClient 实例构建的,在成功和失败完成块中我都调用了 [self removeActiveRequest:netRequest.rpcRequest];作为第一个 Action 。这些块由 AFNetworking 作为默认行为在主线程上执行。

我已经看到死锁发生在必须在队列上持有锁的最后一个屏障块是添加块和删除块。

是否有可能随着系统产生更多线程来支持我的 NSOperationQueue 中的 CheckCacheAndFetchFromNetworkOp Ops,activeRequestsQueue 的优先级太低而无法调度?如果所有线程都被 CheckCacheAndFetchFromNetworkOps 阻塞尝试读取 activeRequests 字典,并且 activeRequestsQueue 阻塞在无法执行的添加/删除屏障块上,则可能会导致死锁。

更新

通过将 NSOperationQueue 设置为 maxConcurrentOperation 计数为 1(或除默认 NSOperationQueueDefaultMaxConcurrentOperationCount 之外的任何合理值)来修复该问题。

基本上我学到的教训是,你不应该有一个默认最大操作计数的 NSOperationQueue 在任何其他 dispatch_queue_t 或 NSOperationQueue 上等待,因为它可能会占用那些其他队列中的所有线程。

这就是正在发生的事情。

队列 - NSOperationQueue 设置为默认 NSDefaultMaxOperationCount,这让系统确定要运行的并发操作数。

操作 - 在 queue1 上运行并在读取后在 AFNetworking 队列上安排网络请求,以确保 RPC 不在 activeRequest 集中。

这是流程:

系统确定它可以支持 10 个并发线程(实际上更像是 80 个)。

一次安排 10 个操作。系统允许 10 个操作在它的 10 个线程上同时运行。所有 10 个操作都调用 hasActiveRequestForRPC,它在 activeRequestQueue 上调度同步块(synchronized block)并阻止 10 个线程。 activeRequestQueue 想要运行它的读取块,但没有任何可用线程。此时我们已经陷入僵局。

更常见的是,我会看到 9 个操作 (1-9) 被调度,其中一个 op1,在第 10 个线程上快速运行 hasActiveRequestForRPC 并调度一个 addActiveRequest barrer 块。然后另一个 op 将在第 10 个线程上被调度,op2-10 将调度并等待 hasActiveRequestForRPC。然后 op1 的预定 addRpc 块将不会运行,因为 op10 占用了最后一个可用线程,并且所有其他 hasActiveRequestForRpc 块将等待屏障块执行。当 op1 尝试在另一个也无法访问任何线程的操作队列上调度缓存操作时,它最终会阻塞。

我假设阻塞 hasActiveRequestForRPC 正在等待 barrer 块执行,但关键是 activeRequestQueue 等待 任何 线程可用性。

最佳答案

编辑:原来问题是 NSOperationQueue 正在调用 enqueueOperation:正在使用所有可用线程,因此因为它们都在等待(通过 dispatch_sync)在 activeRequestsQueue 上发生某些事情.减少这个队列上的 maxConcurrentOperations 解决了这个问题(见评论),尽管这不是一个很好的解决方案,因为它对内核数量等做了假设。更好的解决方案是使用 dispatch_async而不是 dispatch_sync ,虽然这会使代码更复杂。

我之前的建议:

  • 您正在拨打 dispatch_sync(activeRequestsQueue, ...)当您已经在 activeRequestsQueue 上时(并且您的断言由于某种原因没有触发,就像您正在发布一样。)
  • [activeRequests removeObjectForKey:key];导致请求被释放,并且 dealloc 正在等待调用 activeRequestForRpc: 的东西,这会导致死锁。
  • 关于objective-c - 为什么自定义并发队列上的 dispatch_sync 会死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13962885/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com