我们如何使用dispatch_barrier_async
或基于NSOperationQueue
的任何用户定义数据结构来实现NSOperationQueue
的等效行为?
要求是,每当提交屏障操作时,它应该等到之前提交的所有非屏障操作完成执行并阻止之后提交的其他操作。< / p>
注意:不使用 GCD ,因为它不提供(或至少很难)对操作的大量访问,例如取消单个操作等。
答案 0 :(得分:4)
答案 1 :(得分:4)
这或多或少是jeffamaphone所说的,但我提出了一个gist,应该粗略地按照你的要求做。{/ p>
我创建NSMutableArray
个NSOperationQueue
,用作“队列队列”。每次添加BarrierOperation
对象时,都会在最后添加一个新的挂起的操作队列。这将成为addingQueue
,您可以向其添加后续操作。
- (void)addOperation:(NSOperation *)op {
@synchronized (self) {
if ([op isKindOfClass:[BarrierOperation class]]) {
[self addBarrierOperation:(id)op];
} else {
[[self addingQueue] addOperation:op];
}
}
}
// call only from @synchronized block in -addOperation:
- (void)addBarrierOperation:(BarrierOperation *)barrierOp {
[[self addingQueue] setSuspended:YES];
for (NSOperation *op in [[self addingQueue] operations]) {
[barrierOp addDependency:op];
}
[[self addingQueue] addOperation:barrierOp];
// if you are free to set barrierOp.completionBlock, you could skip popCallback and do that
__block typeof(self) weakSelf = self;
NSOperation *popCallback = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf popQueue];
}];
[popCallback addDependency:barrierOp];
[[self addingQueue] addOperation:popCallback];
[[self addingQueue] setSuspended:NO];
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue setSuspended:YES];
[_queueOfQueues addObject:opQueue]; // fresh empty queue to add to
}
当一个NSOperationQueue
完成时,它会弹出,下一个开始运行。
- (void)popQueue
{
@synchronized (self) {
NSAssert([_queueOfQueues count], @"should always be one to pop");
[_queueOfQueues removeObjectAtIndex:0];
if ([_queueOfQueues count]) {
// first queue is always running, all others suspended
[(NSOperationQueue *)_queueOfQueues[0] setSuspended:NO];
}
}
}
我可能错过了至关重要的事情。魔鬼的细节。
这对我来说有点像家庭作业。如果是这样,请告诉我我的成绩。 :)
附录:通过abhilash1912的评论,一个不同但相似的approach。该代码已经测试,因此它已经获胜。但它有点陈旧(今天2年左右;一些不赞成使用的方法)。此外,我怀疑继承NSOperationQueue
是否是最佳途径,尽管它具有保持熟悉的优点。无论如何,如果你已经读过这篇文章,那可能值得一看。
如果您创建或找到世界上最棒的BarrierQueue课程,请在评论或其他方面告知我们,以便将其链接起来。
答案 2 :(得分:3)
我认为不可能创建一个NSOperation
对象,它会为您提供相同类型的功能,障碍更多地与队列的运行方式有关。
使用屏障和NSOperations的依赖机制之间的主要区别是,在屏障的情况下,线程队列等待所有运行并发操作完成,然后它运行屏障阻止,同时确保提交的任何新块和等待的任何块都不会运行,直到关键块通过。
使用NSOperationQueue
,无法以这样的方式设置队列,以便它强制执行适当的障碍:在您的队列之前添加所有NSOperation
个队列必须将关键NSOperation
显式注册为关键作业的依赖项,并且一旦关键作业启动,您必须明确保护NSOperationQueue
以确保在关键作业之前没有其他客户端将作业推送到其上已完成;通过将关键作业添加为后续操作的依赖项来保护队列。
(如果您知道一次只有一项关键工作,这听起来很简单,但任何时候都可能有n
个关键工作,这意味着要跟踪提交订单作业,管理关键作业相对于其依赖作业的相对依赖性 - 一些关键作业可以等待其他作业,一些必须以特定顺序执行相对于其他作业... yikes。)
通过设置NSOperationQueue
并发作业最大值为1,可能会获得这种级别的功能,但我认为这种做法违背了这样做的目的。您也可以通过将NSOperationQueue
包装在一个外观对象中来保护NSOperations
,这些内容可以保护正确提交的内容,并且可以帮助您完成这项工作。&#34;
答案 3 :(得分:0)
另一种方式......不要伤害我。
Todo:保存原点完成,self.maxConcurrentOperationCount = 1在添加时将队列设置为serial。但是应该在执行之前。
#import "NSOperationQueue+BarrierOperation.h"
@implementation NSOperationQueue (BarrierOperation)
- (void)addOperationAsBarrier:(NSOperation *)op
{
//TODO: needs to save origin completion
// if (op.completionBlock)
// {
// originBlock = op.completionBlock;
// }
NSOperationQueue* qInternal = [NSOperationQueue new];
NSInteger oldMaxConcurrentOperationCount = self.maxConcurrentOperationCount;
op.completionBlock = ^{
self.maxConcurrentOperationCount = oldMaxConcurrentOperationCount;
NSLog(@"addOperationAsBarrier maxConcurrentOperationCount restored");
};
[self addOperationWithBlock:^{
self.maxConcurrentOperationCount = 1;
NSLog(@"addOperationAsBarrier maxConcurrentOperationCount = 1");
}];
[qInternal addOperationWithBlock:^{
NSLog(@"waitUntilAllOperationsAreFinished...");
[self waitUntilAllOperationsAreFinished];
}];
NSLog(@"added OperationAsBarrier");
[self addOperation:op];
}
@end