dispatch_async中的synchronized块

时间:2013-10-11 15:42:16

标签: ios objective-c grand-central-dispatch

我见过将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。在什么情况下你想这样做?是不是已经提供所需同步的串行队列?

可以用另一个GCD调度替换同步块吗?

3 个答案:

答案 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];
    });
}