2013-03-27 33 views
1

我在Objective-C中實現Future類,類似於Java的java.util.concurrent.Future,但更適合我的簡單需求。下面是執行:在Objective-C中實現未來:檢測到意外的死鎖

typedef id (^TaskBlock)(); 

static int NotDone = 0; 
static int Done = 1; 

@implementation Future 

- (id)initWithBlock:(TaskBlock)aBlock { 
    if((self = [super init])) { 
     lock = [[NSConditionLock alloc] initWithCondition:NotDone]; 

     block = aBlock; 
    } 

    return self; 
} 

- (void)set:(id)aValue { 
    if ([lock tryLockWhenCondition:NotDone]) { 
     [lock lockWhenCondition:NotDone]; 
     value = aValue; 
     [lock unlockWithCondition:Done]; 
    } 
} 

- (id)get { 
    DLog(@"Retrieving future value"); 

    [lock lockWhenCondition:Done]; 
    id v = value; 
    [lock unlock]; 

    return v; 
} 

- (void)start { 
    dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
     DLog(@"Requesting future value"); 

     [self set:block()]; 

     DLog(@"Future value set"); 
    }); 
} 

@end 

當我使用它,一個線程首先被擋在get:像這樣(等待由block代表完成冗長的計算):

frame #0: 0x9139783e libsystem_kernel.dylib`__psynch_cvwait + 10 
frame #1: 0x904a4e78 libsystem_c.dylib`_pthread_cond_wait + 914 
frame #2: 0x904a4f7b libsystem_c.dylib`pthread_cond_timedwait_relative_np + 47 
frame #3: 0x00b565f2 Foundation`-[NSCondition waitUntilDate:] + 389 
frame #4: 0x00b26fc0 Foundation`-[NSConditionLock lockWhenCondition:beforeDate:] + 285 
frame #5: 0x00b26e9d Foundation`-[NSConditionLock lockWhenCondition:] + 69 
frame #6: 0x0000c335 Jungle`-[Future get](self=0x0757e220, _cmd=0x0000ec3a) + 85 at Future.m:52 
frame #7: 0x0000ab23 Jungle`-[GADelayedBinding waitDelayed](self=0x071c39e0, _cmd=0x0000eb1d) + 99 at GADelayedBinding.m:44 
frame #8: 0x00007a26 Jungle`-[GATuple waitDelayed](self=0x071baa70, _cmd=0x0000eb1d) + 390 at GATuple.m:99 
frame #9: 0x000098fa Jungle`-[GAUnifier unify:with:dynamicContext:](self=0x07561030, _cmd=0x0000eaf8, query=0x071bcc50, base=0x07182f40, dynamicContext=0x00000000) + 2442 at GAUnifier.m:85 
frame #10: 0x00009b06 Jungle`-[GAUnifier unify:with:](self=0x07561030, _cmd=0x0000e71e, query=0x071bcc50, base=0x07182f40) + 118 at GAUnifier.m:93 
frame #11: 0x00002b44 Jungle`__32-[ViewController viewDidAppear:]_block_invoke(.block_descriptor=0x0756a6a0) + 84 at ViewController.m:38 
frame #12: 0x04a0a53f libdispatch.dylib`_dispatch_call_block_and_release + 15 
frame #13: 0x04a1c014 libdispatch.dylib`_dispatch_client_callout + 14 
frame #14: 0x04a0d2e8 libdispatch.dylib`_dispatch_root_queue_drain + 335 
frame #15: 0x04a0cfcb libdispatch.dylib`_dispatch_worker_thread3 + 20 
frame #16: 0x904a2b24 libsystem_c.dylib`_pthread_wqthread + 346 

當計算結束,我收到一個關於這裏的死鎖錯誤:

frame #0: 0x00b9f80f Foundation`_NSLockError 
frame #1: 0x00b26f6f Foundation`-[NSConditionLock lockWhenCondition:beforeDate:] + 204 
frame #2: 0x00b26e9d Foundation`-[NSConditionLock lockWhenCondition:] + 69 
frame #3: 0x0000c268 Jungle`-[Future set:](self=0x0757e220, _cmd=0x0000ec35, aValue=0x07578b40) + 136 at Future.m:43 
frame #4: 0x0000c501 Jungle`__15-[Future start]_block_invoke(.block_descriptor=0x0757e330) + 113 at Future.m:63 
frame #5: 0x04a0a53f libdispatch.dylib`_dispatch_call_block_and_release + 15 
frame #6: 0x04a1c014 libdispatch.dylib`_dispatch_client_callout + 14 
frame #7: 0x04a0d2e8 libdispatch.dylib`_dispatch_root_queue_drain + 335 
frame #8: 0x04a0cfcb libdispatch.dylib`_dispatch_worker_thread3 + 20 
frame #9: 0x904a2b24 libsystem_c.dylib`_pthread_wqthread + 346 

我該怎麼做?我的鎖正在等待的所有條件對我來說都是正確的。

回答

2

該方法tryLockWhenCondition:實際上獲得鎖定,因爲它試圖鎖定它兩次導致死鎖。

if ([lock tryLockWhenCondition:NotDone]) { 
    [lock lockWhenCondition:NotDone]; //Remove this 

我想也指出你正在試圖做的是當你沒有指定nonatomic屬性的默認行爲。

+0

我同意 - 如果Cocoa/Xcode爲我們提供'nonatomic'是免費的? – Jay 2013-03-28 07:07:36

+0

謝謝,那正是問題所在。說到'nonatomic',我不認爲它讓我有可能在單獨的線程中執行冗長的計算(參見上面代碼中的'start'方法),而所有其他線程將被阻塞在'get: '方法,是嗎? – 2013-03-28 09:34:08

+0

看起來你並沒有做任何特別的事情。當聲明一個「@屬性」並且不**指定'非原子的'時,那麼跨訪問是原子的。現在如果你需要在getter或setter之外執行一個額外的鎖,那麼是的,你需要做你正在做的事情。 – Joe 2013-03-28 11:54:50