是否有一种方法,synchronized关键字不会阻止主线程

时间:2012-04-24 13:23:06

标签: ios multithreading ios5 thread-safety

想象一下,你想在iOS应用程序的后台做很多事情但是你正确编码它以便你创建线程(例如使用GCD)来执行这个后台活动。

现在如果您需要在某个时刻写更新变量,但是可能会发生此更新或您创建的任何线程。

你显然希望保护该变量,你可以使用关键字@synchronized为你创建锁,但这里是catch(摘自Apple文档)

  

@synchronized()指令锁定一段代码以供a使用   单线程。其他线程被阻塞,直到线程退出   受保护的代码 - 即执行继续执行最后一次   @synchronized()块中的语句。

这意味着如果你同步一个对象并且两个线程同时写它,那么即使主线程也会阻塞,直到两个线程完成它们的数据写入。

将展示所有这些的代码示例:

// Create the background queue
dispatch_queue_t queue = dispatch_queue_create("synchronized_example", NULL);

// Start working in new thread
dispatch_async(queue, ^
               {                   
                   // Synchronized that shared resource
                   @synchronized(sharedResource_)
                   {   
                       // Write things on that resource
                       // If more that one thread access this piece of code:
                       // all threads (even main thread) will block until task is completed.
                       [self writeComplexDataOnLocalFile];
                   }                         
               });

// won’t actually go away until queue is empty
dispatch_release(queue);

所以问题很简单:如何克服这个问题?我们怎样才能在所有线程上安全地添加锁,除了主线程,我知道在这种情况下不需要阻塞?

编辑澄清

正如你们有些人评论的那样,它看起来似乎是合乎逻辑的(这显然是我在使用synchronized时首先想到的),只有两个尝试获取锁的线程应该阻塞,直到它们都完成为止。

然而,在实际情况下测试,似乎并非如此,主线程似乎也遭受锁定。

我使用此机制将内容记录在单独的线程中,以便不阻止UI。但是当我进行密集的日志记录时,UI(主线程)显然受到很大的影响(滚动不是那么顺利)。

这里有两个选项:后台任务太重,即使主线程受到影响(我怀疑),或者同步也会在执行锁定操作时阻塞主线程(我正在开始重新考虑)。

我将使用Time Profiler进一步挖掘。

2 个答案:

答案 0 :(得分:3)

我不确定我是否理解正确,@synchronize不阻止所有线程,只阻止那些想要在块内部执行代码的线程。所以解决方案可能是;不要在主线程上执行代码。

如果您只是想避免让主线程获得锁定,那么您可以这样做(并破坏残骸):

dispatch_async(queue, ^
               {              
                   if(![NSThread isMainThread])
                   {
                       // Synchronized that shared resource
                       @synchronized(sharedResource_)
                       {   
                           // Write things on that resource
                           // If more that one thread access this piece of code:
                           // all threads (even main thread) will block until task is completed.
                           [self writeComplexDataOnLocalFile];
                       }          
                   }
                   else
                       [self writeComplexDataOnLocalFile];               
               });

答案 1 :(得分:3)

我相信你误解了你从Apple文档中引用的以下句子:

  

其他线程被阻塞,直到线程退出受保护的代码...

这并不意味着所有线程都被阻止,只是意味着所有尝试在同一对象上同步的线程(示例中为_sharedResource)都被阻止。

以下引用来自Apple的Thread Programming Guide,它清楚地表明只有在同一对象上同步的线程才会被阻止。

  

传递给@synchronized指令的对象是用于区分受保护块的唯一标识符。如果在两个不同的线程中执行上述方法,则在每个线程上为anObj参数传递一个不同的对象,每个线程都将锁定并继续处理而不被另一个阻塞。但是,如果在两种情况下都传递相同的对象,则其中一个线程将首先获取锁定,另一个线程将阻塞,直到第一个线程完成关键部分。

更新:如果您的后台线程影响了界面的性能,那么您可能需要考虑将一些睡眠放入后台线程中。这应该允许主线程一段时间来更新UI。

我意识到你正在使用GCD,但是,例如,NSThread有几个方法可以暂停该线程,例如-sleepForTimeInterval:。在GCD中,您可以拨打sleep()

或者,您可能还希望将线程优先级更改为较低的优先级。同样,NSThread为此目的拥有setThreadPriority:。在GCD中,我相信你只会为调度的块使用低优先级队列。

相关问题