Barrier Operations in NSOperationQueue

How can we implement equivalent behavior dispatch_barrier_asyncusing NSOperationQueueeither any custom data structure based on NSOperationQueue?

The requirement is that whenever a barrier operation is transmitted, it must wait until all the non-barrier operations presented earlier have completed their execution and block other operations presented after that.

  • Non-barrier operations should be performed simultaneously.
  • Barrier actions must be performed sequentially.

NB: Do not use GCD , since it does not provide (or at least difficult) a lot of access to these operations, for example, canceling a single operation, etc.

+4
source share
4 answers

This is more or less what jeffamaphone was talking about , but I put a gist , which should, in a rough way, do what you ask.

I create NSMutableArrayof NSOperationQueues, which serves as a "queue of queues". Each time you add an object BarrierOperation, you end a new paused queue at the end of the queue. It becomes addingQueueto which you add subsequent operations.

- (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
}

When one ends NSOperationQueue, it appears, and the next starts.

- (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];
        }
    }
}

Perhaps I missed something important. The devil is in the details.

. , , .:)


: Via abhilash1912 , , , , . ( - 2 , ). , , NSOperationQueue , . , , , , .

BarrierQueue, , .

+3

NSOperation, , use:

- (void)addDependency:(NSOperation *)operation

, .

+3

, NSOperation, ​​ , , .

NSOperations , , , , , , .

NSOperationQueue , : NSOperation , NSOperation , , NSOperationQueue, , ; , .

( , , , , , n, , , - , ... yikes.)

, , NSOperationQueue max, , . , NSOperationQueue , NSOperations, "".

+2
source

Another way ... don't hurt me.

Todo: keep the original completion and self.maxConcurrentOperationCount = 1 sets the queue for sequential additions. But before doing it.

#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
0
source

Source: https://habr.com/ru/post/1532491/


All Articles