2013-06-29 29 views
1

当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; 
} 

回答

3

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

有。 切勿对主队列进行同步呼叫。后续操作:不要在主队列中进行同步呼叫。而且,实际上,它可以概括为永远不要进行从任何队列到任何其他队列的同步呼叫。

通过这样做,可以保证主队列不被阻塞。当然,可能有一个例外情况诱使你违反这一规定,甚至是真正无法避免的情况。但是这应该是个例外,因为即使是一个dispatch_sync()(或NSOpQueue waitUntilDone)也有可能发生死锁。

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

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