异步Cocoa - 防止NSOperation中的“简单”(明显)死锁?

时间:2013-06-29 15:32:25

标签: objective-c deadlock nsoperation

当继承NSOperation以完成一小部分工作时,我发现它很容易死锁。下面我有一个玩具示例,很容易理解为什么它永远不会完成。

我似乎只能通过解决方案来思考来自调用者角度的死锁,而不是被调用者。例如,调用者可以继续运行运行循环,而不是等待完成等。如果主线程在操作期间需要同步消息,我想知道是否存在操作子类可以实现的规范解决方案防止这种类型的死锁。我只是刚开始在异步编程中浸泡脚趾......

@interface ToyOperation : NSOperation

@end

@implementation ToyOperation

- (void)main
{
    // Lots of work

    NSString *string = @"Important Message";
    [self performSelector:@selector(sendMainThreadSensitiveMessage:) onThread:[NSThread mainThread] withObject:string waitUntilDone:YES];

    // Lots more work
}

- (void)sendMainThreadSensitiveMessage:(NSString *)string
{
    // Update the UI or something that requires the main thread...
}

@end

- (int)main
{
    ToyOperation *op = [[ToyOperation alloc] init];
    NSOperationQueue *opQ = [[NSOperationQueue alloc] init];
    [opQ addOperations: @[ op ] waitUntilFinished:YES];    // Deadlock

    return;
}

1 个答案:

答案 0 :(得分:3)

  

如果主线程需要在同步期间同步消息   操作,我想知道是否有一个规范的解决方案   操作子类可以实现防止这种类型的操作   锁死。

有。 永远不要对主队列进行同步调用。并且后续:永远不要从主队列进行同步调用。实际上,它可以概括为从不从任何队列到任何其他队列进行同步调用。< / em>的

通过这样做,您可以保证不阻止主队列。当然,可能会有一个特殊的情况诱使你违反这个规则,甚至是真的,确实是不可避免的情况。但这非常应该是例外,因为即使是单个dispatch_sync()(或NSOpQueue waitUntilDone)也有可能发生死锁。

当然,从队列到队列的数据更新可能很棘手。有几种选择;并发安全数据层(非常难),只传递不可变对象或数据副本(通常为了显示目的而传递到主队列 - 相当容易,但可能很昂贵),或者你可以按照基于UUID的故障模式进行传递核心数据使用。无论你如何解决这个问题,与任何其他并发模型相比,问题并不是什么新鲜事。

一个例外是用队列替换锁(例如,不是在内部对类使用@synchronized(),而是使用串行GCD队列,并在必须进行同步操作的任何地方使用dispatch_sync()到该队列。更快更直接。)但这并不是解决一个完全不同的问题的例外。