我见过将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。在什么情况下你想这样做?是不是已经提供所需同步的串行队列?
可以用另一个GCD调度替换同步块吗?
答案 0 :(得分:3)
@synchronized()
确保包含的代码(对于给定标记作为@synchronized
的参数)一次只在一个线程上运行。
提交到串行队列的块一次执行一个,即。在完成执行之前提交的所有块之前,不会执行给定块。只要从串行队列上运行的代码访问共享资源,就不需要同步/锁定对它的访问。但是,仅仅因为给定队列是串行的,并不意味着其他队列/线程(甚至串行队列!)没有同时运行,并且访问相同的共享资源。
使用@synchronized()
是防止这些多线程/队列同时访问资源的一种方法。请注意,访问共享资源的所有代码都需要包含@synchronized()
。
是的,您可以使用另一个GCD调度而不是同步块。执行此操作的“GCD方式”是使用串行队列序列化对共享资源的所有访问。因此,无论何时需要访问共享资源,您都可以将该代码(根据用例使用dispatch_sync()
或dispatch_async()
)分配给与资源关联的串行队列。当然,这意味着资源访问队列必须对访问共享资源的程序的所有部分可见/可访问。 (你基本上与@synchronized()
有同样的问题,因为它的锁令必须可以在任何需要使用的地方访问,但它更容易,因为它可以只是一个字符串常量。)
答案 1 :(得分:1)
队列是,它是同步的,但如果你访问其中的任何“外部”对象,它们就不会同步。
如果有很多线程,你知道每个线程都会打开对象。一个典型的情况是,当您异步执行CoreData导入时,您必须@synchronized上下文或商店协调员。
答案 2 :(得分:1)
线程安全是指使可变共享状态成为不可变或非共享。在这种情况下,synchronize
和串行队列是临时取消共享(防止并发访问)的方法,两者都是有效的。
但是,请考虑在同一对象中有相关信息的不相交集的情况。例如,包含地址(城市,街道等)的1)部分,以及2)价格,税收,折扣的账单。两者都需要受到保护以避免不一致状态(对象A设置新街道,而对象B读取旧城市和新街道),但两者都不相关。在这种情况下,您应该使用较低级别的粒度来避免不相关代码之间的阻塞。
因此规则是:不要在同一对象内的不相关变量集上使用同步,因为它会导致它们之间不需要的块。您可以使用队列+同步或每组队列,但不能在两个集合上同步。
关键是synchronize
指的是对象的唯一内在锁,并且该令牌只能被保持一次。它实现了相同的目标,就像您通过一个队列路由所有相关代码一样,除了您可以有多个队列(因此,更低的粒度),但只有一个内部锁。
回到你的例子。假设对象被记录为“状态X受synchronize
保护”,则在队列中使用synchronize
对于阻止可以访问相同状态的相关方法很有用。因此,队列和同步可能会保护不同的东西,或者串行队列可以执行不同的任务。
优先选择队列的另一个原因是编写一个更复杂的模式,如读写锁。例如:
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];
});
}