2011-09-08 135 views
4

我读过应该使用GCD同步队列(dispatch_sync)来实现代码的关键部分。一个例子是从账户余额中减去交易金额的块。同步调用的有趣部分是一个问题,它是如何影响多线程上其他块的工作的?是否在同步GCD队列上放置一个阻止阻塞并暂停其他阻塞的阻塞?

让我们想象一下,有3个线程在异步模式下使用并执行主队列和自定义队列中的系统和用户定义块。这些块都按照某种顺序并行执行。现在,如果一个块放在一个具有同步模式的自定义队列中,是否意味着所有其他块(包括其他线程)都被挂起,直到成功执行块为止?或者这意味着只有一些锁将被放在该块上,而其他锁仍然会执行。但是,如果其他块使用与同步块相同的数据,那么其他块将不可避免地等待,直到该锁将被释放。

恕我直言,这并不重要,它是一个或多个核心,同步模式应冻结整个应用程序的工作。但是,这些只是我的想法,所以请评论和分享你的见解:)

+0

没有“同步队列”。有同步调度(等待块完成)和异步调度(只是将该块添加到队列中)。并发队列和并行队列。连续队列不能用于_implement_关键部分,但它们对于避免关键部分的需要非常有用。 – gnasher729

回答

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. 

而且有两种调度队列,串行和并行的。序列号按照它们被添加的顺序严格地逐个分派块。当一个完成时,另一个开始。这种执行只需要一个线程。并发队列同时并行地分派块。那里有更多的线程在使用。

如果您认为合适,您可以混合使用sync/async dispatch和串行/并发队列。如果您想要使用GCD来保护对关键部分的访问,请使用单个串行队列并分配此队列上共享数据的所有操作(同步或异步,无关紧要)。这样,总会有仅一个街区与共享数据进行操作:

- (void) addFoo: (id) foo { 
    dispatch_sync(guardingQueue, ^{ [sharedFooArray addObject:foo]; }); 
} 

- (void) removeFoo: (id) foo { 
    dispatch_sync(guardingQueue, ^{ [sharedFooArray removeObject:foo]; }); 
} 

现在,如果guardingQueue是一个串行队列,添加/删除操作不能发生冲突,即使addFoo:removeFoo:方法都是从不同的同时调用线程。

+0

那么两个块(一个asyn A和另一个同步B)在不同线程上执行但两个块使用同一个对象(例如具有余额的帐户)的情况如何呢?如果同步B块在中间时间线开始并更新余额,而A块尝试读取帐户,那么是否会出现竞赛?我们是否明确地做了一些事情来阻止块A读取数据? – Centurion

+0

据我了解你的问题,使用串行队列和调度同步模式块只是冻结调用者代码,直到块完成。没有其他的保护措施来锁定共享资源的读/写。 – Centurion

+0

谈论同步/异步块或队列是没有意义的。同步或异步就是您在队列中分派块的方式。我已经更新了答案,以解释如何保护GCD的关键部分,希望有所帮助。 – zoul

2

不,它不。

同步的部分是块放在队列中,但控制不返回调用函数,直到块返回。

GCD的许多用途是异步的;你把一个块放在一个队列中,而不是等待块完成它的工作控制传回给调用函数。

这对其他队列没有影响。

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); 

有了这个,不管你的队列并发的水平,可以保证只有一个线程将随时访问帐户给定的时间。

还有更多类型的同步对象,但这两个很容易使用,并且相当灵活。

+0

正是我需要的!小型打字虽然。 'dispatch_semaphore_wait'有两个参数。像'dispatch_semaphore_wait(accountLock,DISPATCH_TIME_FOREVER);'应该做的伎俩。 –

+0

该文档说明您在完成信号量时应该“dispatch_release”。每次我都会崩溃。不知道我在做什么。无论哪种方式,这个片段中的信号量都在泄漏。 –

+0

你是正确的信号量正在泄漏。你可能会崩溃,因为你试图在错误的时刻释放信号量?假设锁是通过对象创建的,它应该在释放对象时释放。我会在上面的代码中添加注释以达到这个效果。 – aLevelOfIndirection