dispatch_async中的synchronized块
我见过将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。 在什么情况下你想这样做? 是不是已经提供所需同步的串行队列?
可以用另一个GCD调度替换同步块吗?
@synchronized()
确保包含的代码(对于给定的标记作为@synchronized
的参数)一次只能在一个线程上运行。
提交给串行队列的块一次执行一个,即。 在完成执行之前提交的所有块之前,不会执行给定块。 只要从串行队列上运行的代码访问共享资源,就不需要同步/锁定对它的访问。 但是,仅仅因为给定队列是串行的,并不意味着其他队列/线程(甚至串行队列!)没有同时运行,并且访问相同的共享资源。
使用@synchronized()
是防止这些multithreading/队列同时访问资源的一种方法。 请注意,访问共享资源的所有代码都需要使用@synchronized()
进行包装。
是的,您可以使用另一个GCD调度而不是同步块。 执行此操作的“GCD方式”是使用串行队列序列化对共享资源的所有访问。 因此,无论何时需要访问共享资源,您都可以将该代码(根据用例使用dispatch_sync()
或dispatch_async()
dispatch_sync()
dispatch_async()
资源关联的串行队列。 当然,这意味着资源访问队列必须对访问共享资源的程序的所有部分可见/可访问。 (你基本上和@synchronized()
有同样的问题,因为它的锁令必须可以在任何需要使用的地方访问,但它更容易,因为它只是一个字符串常量。)
队列是,它是同步的,但如果您访问其中的任何“外部”对象,它们将不同步。
如果有很multithreading,你知道每个线程都会打开对象。 一个典型的情况是,当您异步执行CoreData导入时,您必须@synchronized上下文或商店协调员。
线程安全是关于使可变共享状态不可变或不共享。 在这种情况下, 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]; }); }