我已经看到将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。你想在什么情况下这样做?串行队列不是已经提供了所需的同步吗?
可以用另一个 GCD 调度替换同步块吗?
我已经看到将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。你想在什么情况下这样做?串行队列不是已经提供了所需的同步吗?
可以用另一个 GCD 调度替换同步块吗?
@synchronized()
确保包含的代码(对于作为参数的给定标记@synchronized
)一次仅在一个线程上运行。
提交到串行队列的块一次执行一个,即。在完成执行之前提交的所有块之前,不会执行给定块。只要仅从串行队列上运行的代码访问共享资源,就无需同步/锁定对它的访问。但是,仅仅因为给定的队列是串行的,并不意味着其他队列/线程(甚至串行队列!)不会同时运行并访问相同的共享资源。
使用@synchronized()
是防止这些多个线程/队列同时访问资源的一种方法。请注意,所有访问共享资源的代码都需要用@synchronized()
.
是的,您可以使用另一个 GCD 调度来代替同步块。这样做的“GCD 方式”是使用串行队列序列化对共享资源的所有访问。因此,任何时候需要访问共享资源,您都可以将该代码(使用任一dispatch_sync()
或dispatch_async()
取决于用例)分派到与资源关联的串行队列。当然,这意味着资源访问队列必须对访问共享资源的程序的所有部分可见/可访问。(本质上你有同样的问题@synchronized()
,它的锁定令牌必须在任何需要使用的地方都可以访问,但它更容易一些,因为它可以只是一个字符串常量。)
Thread-safe is about making mutable shared state either immutable, or unshared. In this case, synchronize
and serial queues are ways to temporarily unshare (prevent concurrent access), and both are valid.
However, consider the case where you have disjoint sets of related info inside the same object. For example, a bill with 1) parts of an address (city, street, etc), and 2) price, taxes, discount. Both need to be protected to avoid inconsistent state (object A sets a new street, while object B reads the old city and the new street), but both are unrelated. In this case, you should use the lower level of granularity to avoid blocks between unrelated code.
So a rule would be: don't use synchronize on unrelated sets of variables inside the same object, because it would cause unneeded blocks between them. You can use a queue + synchronize, or a queue per set, but not synchronized on both sets.
The key is that synchronize
refers to the one and only intrinsic lock of the object, and that token can only be held by once. It accomplishes the same goal as if you routed all related code through one queue, except that you can have multiple queues (and thus, lower granularity), but only one intrinsic lock.
Back to your example. Assuming that the object is documented as “state X is protected by synchronize
”, the use of synchronize
inside the queue is useful to block related methods that could access that same state. So maybe queue and synchronized are protecting different things, or the serial queue is there to perform a different task.
Another reason to prefer a queue is to write a more sophisticated pattern like a read-write lock. Example:
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];
});
}
队列是的,它是同步的,但是如果您访问其中的任何“外部”对象,它们不会同步。
如果有很多线程,您就知道每个线程都会轮到对象。一个典型的情况是,当您异步执行 CoreData 导入时,您必须 @synchronized 上下文或存储协调器。