【发布时间】:2013-10-12 06:40:44
【问题描述】:
我见过将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。你想在什么情况下这样做?串行队列不是已经提供了所需的同步吗?
可以用另一个 GCD 调度替换同步块吗?
【问题讨论】:
标签: ios objective-c grand-central-dispatch
我见过将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。你想在什么情况下这样做?串行队列不是已经提供了所需的同步吗?
可以用另一个 GCD 调度替换同步块吗?
【问题讨论】:
标签: ios objective-c grand-central-dispatch
@synchronized() 确保包含的代码(对于作为@synchronized 的参数的给定标记)一次仅在一个线程上运行。
提交到串行队列的块一次执行一个,即。在完成执行之前提交的所有块之前,不会执行给定块。只要仅从串行队列上运行的代码访问共享资源,就无需同步/锁定对它的访问。然而,仅仅因为给定的队列是串行的,并不意味着其他队列/线程(甚至串行队列!)不会同时运行并访问相同的共享资源。
使用@synchronized() 是防止这些多个线程/队列同时访问资源的一种方法。请注意,所有访问共享资源的代码都需要用@synchronized()包装。
是的,您可以使用另一个 GCD 调度来代替同步块。这样做的“GCD 方式”是使用串行队列序列化对共享资源的所有访问。因此,任何时候需要访问共享资源时,您都可以将该代码(根据用例使用dispatch_sync() 或dispatch_async())分派到与资源关联的串行队列。当然,这意味着资源访问队列必须对访问共享资源的程序的所有部分都是可见/可访问的。 (@synchronized() 本质上存在相同的问题,因为它的锁定令牌必须在需要使用的任何地方都可以访问,但它更容易一些,因为它可以只是一个字符串常量。)
【讨论】:
队列是的,它是同步的,但是如果您访问其中的任何“外部”对象,它们都不会同步。
如果有很多线程,你知道每个线程都会轮到对象。一个典型的情况是,当您异步执行 CoreData 导入时,您必须 @synchronized 上下文或存储协调器。
【讨论】:
线程安全是关于使可变共享状态不可变或不共享。在这种情况下,synchronize 和串行队列是临时取消共享(防止并发访问)的方式,两者都是有效的。
但是,请考虑在同一对象内有不相交的相关信息集的情况。例如,账单包含 1) 地址(城市、街道等)的一部分,以及 2) 价格、税金、折扣。两者都需要保护以避免不一致的状态(对象 A 设置新街道,而对象 B 读取旧城和新街道),但两者都是不相关的。在这种情况下,您应该使用较低级别的粒度来避免不相关代码之间的阻塞。
因此,一条规则是:不要对同一对象内不相关的变量集使用同步,因为这会导致它们之间出现不需要的块。您可以使用队列 + 同步,或每组一个队列,但在两个组上不同步。
关键是synchronize 指的是对象的唯一一个内在锁,并且该令牌只能被持有一次。它实现了与通过一个队列路由所有相关代码相同的目标,不同之处在于您可以有多个队列(因此粒度较低),但只有一个内在锁。
回到你的例子。假设对象被记录为“状态 X 受synchronize 保护”,在队列中使用synchronize 有助于阻止可以访问相同状态的相关方法。所以可能 queue 和 synchronized 保护不同的东西,或者串行队列执行不同的任务。
更喜欢队列的另一个原因是编写更复杂的模式,例如读写锁。示例:
NSMutableDictionary *_dic = [NSMutableDictionary new];
dispatch_queue_t _queue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
- (id) objectForKey:(id)key
{
__block obj;
dispatch_sync(_queue, ^{
obj = [_dic objectForKey: key];
});
return obj;
}
- (void) setObject:(id)obj forKey:(id)key
{
// exclusive access while writing
dispatch_barrier_async(_queue, ^{
[_dic setObject:obj forKey:key];
});
}
【讨论】:
@synchronized 阻止了对对象的所有访问。它不是。它锁定与该对象关联的内部锁,但这只会影响尝试获取相同锁的其他代码。也就是说,在同一对象上也使用@synchronized 的其他代码。不使用@synchronized 向对象发送消息的代码不受影响。