1

当子类化 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;
}
4

1 回答 1

3

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

有。 永远不要对主队列进行同步调用。还有一个后续: 永远不要从主队列进行同步调用。 而且,实际上,它可以总结为永远不要从任何队列同步调用任何其他队列。

通过这样做,您可以保证主队列没有被阻塞。当然,可能有一个例外情况会诱使您违反此规则,甚至在某些情况下,它确实确实是不可避免的。但这应该是一个例外,因为即使是单个 dispatch_sync()(或 NSopQueue waitUntilDone)也有可能死锁。

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

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

于 2013-06-29T16:02:31.750 回答