2012-11-23 51 views
2

我正在做這個多線程應用程序,看看@synchronized指令是如何工作的。我讀過如果所有的線程都與@synchronized參數具有相同的對象,那麼他們都等待相同所以我想用self作爲參數,因爲它對於所有線程都是一樣的。
在這個應用程序中有一個文本字段被所有線程多次編輯。 我不關心性能,它只是一個測試所以我不把@synchronized指令放在for之前,但在它之內。簡單的多線程應用程序有時會失敗

我用的屬性:

@property (weak) IBOutlet NSTextField *textField; 
@property (nonatomic, copy) NSNumber* value; 
@property (nonatomic,copy) NSMutableArray* threads; 

代碼:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    value= @0; 
    textField.objectValue= value; 
    for(int i=0; i<10; i++) 
    { 
     NSThread* thread=[[NSThread alloc]initWithTarget: self selector: @selector(routine:) object: nil]; 
     [threads addObject: thread]; 
     [thread start]; 
    } 
} 

- (void) routine : (id) argument 
{ 
    for(NSUInteger i=0; i<100; i++) 
    { 
     @synchronized(self) 
     { 
      value= @(value.intValue+1); 
      textField.objectValue= value; 
     } 
    } 
} 

有時應用程序有成功,我看到1000作爲文本字段value.But有時沒有,我擔心這是飢餓和我沒有看到文本字段上的任何東西,它是空的。我嘗試調試,但很難看到什麼是錯誤的,因爲失敗的標準似乎對我來說是偶然的,有時它可以正常工作。

SOLUTION

@synthesize threads,value, textField; 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    value= @0; 
    threads=[[NSMutableArray alloc]initWithCapacity: 100]; 
    for(NSUInteger i=0; i<100; i++) 
    { 
     NSThread* thread=[[NSThread alloc]initWithTarget: self selector: @selector(routine:) object: nil ]; 
     [threads addObject: thread]; 
     [thread start]; 
    } 
} 

- (void) routine : (id) arg 
{ 
    for(NSUInteger i=0; i<1000; i++) 
    { 
     @synchronized(self) 
     { 
      value= @(value.intValue+1); 
      [textField performSelectorOnMainThread: @selector(setObjectValue:) withObject: value waitUntilDone: NO]; 
     } 
    } 
} 
+1

這可能嗎?我有沒有發現一個好問題?還是我只是在做夢? (+1爲一個有趣的問題。) – 2012-11-23 22:28:36

+1

不知道..代碼看起來好嗎..但我不知道appkit的哪些部分是線程安全的... –

+1

您添加的解決方案是不正確的。 [通知在同一個線程上發佈和接收。](http://stackoverflow.com/questions/1004589/nsnotificationcenter-do-objects-receive-notifications-on-the-same-thread-they-a)設置一個斷點在'-changeValue:'上,你會看到當前線程不是主線程。 –

回答

3

您正在訪問一個NSTextField,這是NSView一個亞類中,從非主線程。 That is not a safe thing to do。結果是不確定的;有時它似乎起作用,有時它不起作用。

+0

什麼是替代解決方案?我是否需要通過發送消息來調用主線程並使其執行選擇器? –

+1

是的,使用' - [NSObject performSelectorOnMainThread:withObject:waitUntilDone:]'或GCD在主線程上運行你的代碼。 [這裏是一個例子。](http://stackoverflow.com/questions/7290931/gcd-threads-program-flow-and-ui-updating/7291056#7291056)搜索像「NSView線程更新」應該會引導你到更多的例子。 –

+1

另請注意:向'[NSNotificationCenter defaultCenter]'發佈通知不是進入主線程的一種方式。通知的觀察者在發佈通知的同一線程上同步調用。 –

1

你總是在後臺線程中更新你的UI。不是很好。你應該這樣做;

(void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    value= @0; 
    textField.objectValue= value; 
    for(int i=0; i<10; i++) 
    { 
     NSThread* thread=[[NSThread alloc] initWithTarget: self selector: @selector(routine:) object: nil]; 
     [threads addObject: thread]; 
     [thread start]; 
    } 
} 

- (void) routine : (id) argument 
{ 
    for(NSUInteger i=0; i<100; i++) 
    { 
     @synchronized(self) 
     { 
      value= @(value.intValue+1); 
      [textField performSelectorOnMainThread:@selector(setObjectValue:) withObject:value waitUntilDone: NO];  
     } 
    } 
} 

嘗試鎖定實例並使用sleep方法使其更平滑。

要鎖定後臺線程中的變量,首先鎖定它,然後設置它的值並將其解鎖爲;

[NSLock lock]; 
[email protected](value.intValue+1) 
[NSLock unlock]; 

但是,您已經擁有@synchronized,這使得它可以保護從多個線程同時訪問的變量。我認爲@synchonized塊更容易理解。

睡眠在這種情況下更合適。如果您在線程中放置2秒鐘的睡眠,那麼您可以看到textField每2秒更改一次,並且更明顯,更有意義。

[NSThread sleepForTimeInterval:2000] //在更新textField並查看效果後,將其放置在循環中。

另外,最好創建一個runloop並在一些runloop中執行這個線程,這是一個更好的做法。