在iOS多线程中,我们可以用GCD的串行队列来实现同步锁的效果。通过在把任务添加到串行队列中来依次执行,达到同步的效果。
同时GCD又提供了两个函数,来方便我们实现这个同步效果。他们就是dispatch_barrier_async 和 dispatch_barrier_sync
先看官方文档
Function dispatch_barrier_async
Submits a barrier block for asynchronous execution and returns immediately.
提交一个异步执行且立即返回的栅栏block
Declaration
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
Parameters
The dispatch queue on which to execute the barrier block. The queue is retained by the system until the block has run to completion. This parameter cannot be NULL
.
执行栅栏block的调度队列。系统会持有这个队列直到这个block执行完成。这个参数不能为NULL
The barrier block to submit to the target dispatch queue. This block is copied and retained until it finishes executing, at which point it is released. This parameter cannot be NULL
.
提交到目标调度队列的栅栏block。block会被拷贝和持有直到它执行完成,完成后会被释放。这个参数不能为NULL
Discussion
Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.
当这个block被提交到队列后就立即返回,并不会等待这个block的调用。当栅栏block到达指定的并发队列的时候,这个block不会立即执行。相反,这个队列会等待当前正在执行的blocks完成执行。这时,栅栏block自行执行。一些后面提交的blocks会等到栅栏block执行完成之后才会开始执行。
The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create
function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async
function.
指定的queue应该是你通过函数 dispatch_queue_create
创建的并发队列。如果你传入一个串行队列或者全局并发队列。这个方法就像dispatch_async
一样。
Function dispatch_barrier_sync
Submits a barrier block object for execution and waits until that block completes.
提交一个栅栏block执行,等待这个block执行完成。
Declaration
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
Parameters
The dispatch queue on which to execute the barrier block. This parameter cannot be NULL
.
执行栅栏block的指定队列。参数不能为NULL
。
The barrier block to be executed. This parameter cannot be NULL
.
执行的栅栏block。参数不能为NULL
。
Discussion
Submits a barrier block to a dispatch queue for synchronous execution. Unlike dispatch_barrier_async
, this function does not return until the barrier block has finished. Calling this function and targeting the current queue results in deadlock.
提交一个同步执行的栅栏block到指定队列。不像dispatch_barrier_async
,这个方法直到栅栏block执行完成后才会返回。调用这个函数在当前线程会导致死锁。
注:队列引起的循环等待导致死锁
When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the queue executes the barrier block by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.
当这个block被提交到队列后就立即返回,并不会等待这个block的调用。当栅栏block到达指定的并发队列的时候,这个block不会立即执行。相反,这个队列会等待当前正在执行的blocks完成执行。这时,栅栏block自行执行。一些后面提交的blocks会等到栅栏block执行完成之后才会开始执行
The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create
function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_sync
function.
指定的queue应该是你通过函数 dispatch_queue_create
创建的并发队列。如果你传入一个串行队列或者全局并发队列。这个方法就像dispatch_sync
一样。
Unlike with dispatch_barrier_async
, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy
is performed on the block.
不像 dispatch_barrier_async
,目标队列上不会执行retain操作。因为调用这个函数是同步,他不会被调用者“借用”。此外,对这个block不会执行Block_copy
。
As an optimization, this function invokes the barrier block on the current thread when possible.
为了优化,这个函数应该尽可能的调用当前线程的栅栏block。
其实把整个文档一翻译完,我想要说的就已经全部包括了,所以遇到问题,先看官方的文档是不错的选择。
另外我想说一下在iOS中用得比较多的一个场景,多读单写。
对于一个对象中的某个属性,我们可以自定义两个方法来实现对其的多读单写操作。
-(NSUInteger)ticketCount {
__block NSUInteger count;
//因为要立即返回,所以我们用dispatch_sync
dispatch_sync(_concurrent_queue, ^{
count = self->_ticketCount;
});
return count;
}
- (void)setTicketCount:(NSUInteger)ticketCount {
//对于写操作,我们这里用dispatch_barrier_async,用栅栏函数实现线程安全
dispatch_barrier_async(_concurrent_queue, ^{
if (ticketCount != self->_ticketCount) {
self->_ticketCount = ticketCount;
}
});
}
其实一般我们不会通过属性的getter和setter方法来写多读单写,一般会自己定义两个方法,在这两个方法中,不一定仅仅是简单的读写,也可以定义一些自己的逻辑。例如我们卖火车票的一个例子。
-(NSUInteger)ticketCount {
__block NSUInteger count;
dispatch_sync(_concurrent_queue, ^{
count = self->_ticketCount;
});
return count;
}
-(NSInteger)sellTicketCount:(NSUInteger)count withUserId:(NSUInteger)uid {
__block NSInteger sellCount;
dispatch_barrier_sync(_concurrent_queue, ^{
if ([self queryUserHaveBoughtWithUserId:uid]) {
//是否购买过
sellCount = -1; //-1代表已经买过票了
} else {
if (count > self->_ticketCount) {
sellCount = self->_ticketCount;
self->_ticketCount = 0;
} else {
sellCount = count;
self->_ticketCount = self->_ticketCount - count;
}
BoughtUser *buyer = [[BoughtUser alloc] init];
buyer.uid = uid;
buyer.count = sellCount;
[self->_haveBoughtUsers addObject:buyer];
}
});
return sellCount;
}
- (BOOL)refundTicketCount:(NSUInteger)count withUserId:(NSUInteger)uid {
__block BOOL isSuccess = NO;
dispatch_barrier_sync(_concurrent_queue, ^{
if ([self queryUserBoughtCountWithUserId:uid] == count) {
//只能退还全部车票
self->_ticketCount = self->_ticketCount + count;
isSuccess = YES;
}
});
return isSuccess;
}
这也算一个多读单写的具体实现。这里因为要返回购买后剩余的票数,所以只能用dispatch_barrier_sync函数。
需要注意的一定要传入自己创建的并发队列