2012-11-23 36 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中执行这个线程,这是一个更好的做法。