2

想象一下,您想在 iOS 应用程序的后台做很多事情,但您正确地对其进行了编码,以便您创建线程(例如使用 GCD)来执行此后台活动。

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

您显然想保护该变量,您可以使用关键字@synchronized为您创建锁,但这里有一个问题(摘自 Apple 文档)

@synchronized()指令锁定一段代码供单个线程使用。其他线程被阻塞,直到线程退出受保护的代码——也就是说,当执行继续超过@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);

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

编辑澄清

正如你们中的一些人评论的那样,只有两个试图获取锁的线程应该阻塞,直到它们都完成,这似乎是合乎逻辑的(这显然是我在使用同步时最初的想法)。

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

我使用这种机制在单独的线程中记录事物,这样 UI 就不会被阻塞。但是当我进行密集的日志记录时,UI(主线程)显然受到很大影响(滚动不那么流畅)。

所以这里有两个选择:要么后台任务太繁重以至于连主线程都会受到影响(我对此表示怀疑),或者同步在执行锁定操作时也会阻塞主线程(我正在重新考虑)。

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

4

2 回答 2

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];               
               });
于 2012-04-24T13:29:21.193 回答
3

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

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

这并不意味着所有线程都被阻塞,它只是意味着所有试图在同一个对象上同步的线程(_sharedResource在你的例子中)都被阻塞了。

以下引用摘自 Apple 的线程编程指南,它清楚地表明只有在同一对象上同步的线程才会被阻塞。

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

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

我意识到您正在使用 GCD,但是,例如,NSThread有几个方法可以暂停线程,例如-sleepForTimeInterval:. 在 GCD 中,您可能只需调用sleep().

或者,您可能还想考虑将线程优先级更改为较低的优先级。再次,NSThreadsetThreadPriority:为此目的。在 GCD 中,我相信您只会对调度的块使用低优先级队列。

于 2012-04-24T13:33:38.320 回答