想象一下,你想在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进一步挖掘。
答案 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中,我相信你只会为调度的块使用低优先级队列。