我读到应该使用GCD同步队列(dispatch_sync)来实现代码的关键部分。一个例子是从帐户余额中减去交易金额的块。同步调用的有趣部分是一个问题,它如何影响多个线程上其他块的工作?
让我们想象一下,在异步模式下有3个线程使用和执行主队列和自定义队列中的系统和用户定义块。这些块都以某种顺序并行执行。现在,如果将块放在具有同步模式的自定义队列上,这是否意味着所有其他块(包括在其他线程上)都被挂起,直到块成功执行为止?或者这是否意味着只有一些锁定将被放置在该块上,而其他锁定仍将执行。但是,如果其他块使用与同步块相同的数据,那么其他块将不可避免地等到该锁将被释放。
恕我直言无所谓,无论是一个还是多个内核,同步模式都应该冻结整个应用程序的工作。但是,这些只是我的想法,所以请评论并分享您的见解:)答案 0 :(得分:7)
同步调度会暂停代码的执行,直到调度块完成为止。异步调度立即返回,该块与调用代码异步执行:
dispatch_sync(somewhere, ^{ something });
// Reached later, when the block is finished.
dispatch_async(somewhere, ^{ something });
// Reached immediately. The block might be waiting
// to be executed, executing or already finished.
有两种调度队列,串行和并发。串行的按照添加顺序严格逐个发送块。当一个完成时,另一个开始。这种执行只需要一个线程。并发队列并行地同时调度块。那里有更多的线程。
您可以根据需要混合和匹配同步/异步调度和串行/并发队列。如果要使用GCD来保护对关键部分的访问,请使用单个串行队列并对此队列上的共享数据执行所有操作(同步或异步,无关紧要)。这样,共享数据总会只有一个块运行:
- (void) addFoo: (id) foo {
dispatch_sync(guardingQueue, ^{ [sharedFooArray addObject:foo]; });
}
- (void) removeFoo: (id) foo {
dispatch_sync(guardingQueue, ^{ [sharedFooArray removeObject:foo]; });
}
现在,如果guardingQueue
是一个串行队列,即使从不同的线程同时调用addFoo:
和removeFoo:
方法,添加/删除操作也不会发生冲突。
答案 1 :(得分:2)
不,不。
同步部分是将块放在队列中,但是在块返回之前,控制不会传递回调用函数。
GCD的许多用途都是异步的;你把一个块放在队列上而不是等待块完成它的工作控制被传递回调用函数。
这对其他队列没有影响。
答案 2 :(得分:1)
如果您需要序列化对某个资源的访问权限,那么至少有两个 您可以访问的机制。如果您有一个帐户对象(这是唯一的 对于给定的帐号),您可以执行以下操作:
@synchronize(accountObject) { ... }
如果您没有对象但使用的C结构只有一个 对于给定帐号的此类结构,您可以执行以下操作:
// Should be added to the account structure.
// 1 => at most 1 object can access accountLock at a time.
dispatch_semaphore_t accountLock = dispatch_semaphore_create(1);
// In your block you do the following:
block = ^(void) {
dispatch_semaphore_wait(accountLock,DISPATCH_TIME_FOREVER);
// Do something
dispatch_semaphore_signal(accountLock);
};
// -- Edited: semaphore was leaking.
// At the appropriate time release the lock
// If the semaphore was created in the init then
// the semaphore should be released in the release method.
dispatch_release(accountLock);
有了这个,无论您的队列的并发级别如何,您都可以保证在任何给定时间只有一个线程可以访问帐户。
还有更多类型的同步对象,但这两种对象易于使用 非常灵活。