简体   繁体   中英

NSOperationQueue add new operation when completion block is successful

I am trying to complete few operations in a serial order on a background thread.

the function I am calling has already a completion block, so when the function is completed I want to call the same function with a new argument.

so basically consecutive operations.

dispatch_async or DISPATCH_QUEUE_SERIAL , fires the functions in correct order but doesn't care if the first function is finished yet before calling next one so I don't wanna use them.

NSOperationQueue* serialQ = [[NSOperationQueue alloc] init]; serialQ.maxConcurrentOperationCount = 1; makes more sense to me. so when first function is started to compute second function on the queue has to wait until it finished its completion block.

NSOperationQueue* serialQ = [[NSOperationQueue alloc] init];
    serialQ.maxConcurrentOperationCount = 1; //this will set this queue to Serial



        for (File *file in queueArrray) {
            Streamer *streamer=[[Streamer alloc] init];

            NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init];

            __weak NSBlockOperation *weakDownloadOperation = downloadOperation;

            [weakDownloadOperation addExecutionBlock:^{
                [streamer loadFile:file withCallback:^(NSString *error, BOOL success) {
                    if (success) {
                        NSLog(@"file loaded %@",file.fileUrl);
                        //here start the next operation !!!!!!!!
                        ??????????????????????????????????????
                    }
                }];
            }];  
        }

Serial queue infact makes sure that the first block added to the queue to execute is complete before the second block executes.

-(void)testSerialQueue
{
    dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_async(serialQueue, ^{
        [self countTo100];
    });

    dispatch_async(serialQueue, ^{
        [self countFrom200To400];
    });

    dispatch_async(serialQueue, ^{
        [self countFrom400To500];
    });

}

- (void)countTo100
{
    for (int i = 0; i < 100; i++) {
        NSLog(@"%d", i);
    }
}

- (void)countFrom200To400
{
    for (int i = 200; i < 400; i++) {
        NSLog(@"%d", i);
    }
}

- (void)countFrom400To500
{
    for (int i = 400; i < 500; i++) {
        NSLog(@"%d", i);
    }
}

If you look at the log from above it will print serially from 0 .. 100 first and then 200 .. 400 and 400 .. 500.

Now, consider the following snippet, where you execute each methods inside a concurrent block like,

-(void)testSerialQueue
{
    dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_async(serialQueue, ^{
        [self countTo100];
    });

    dispatch_async(serialQueue, ^{
        [self countFrom200To400];
    });

    dispatch_async(serialQueue, ^{
        [self countFrom400To500];
    });

}

- (void)countTo100
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%d", i);
        }
    });

}

- (void)countFrom200To400
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{
        for (int i = 200; i < 400; i++) {
            NSLog(@"%d", i);
        }
    });
}

- (void)countFrom400To500
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{
        for (int i = 400; i < 500; i++) {
            NSLog(@"%d", i);
        }
    });
}

Here, you added all the methods inside serial queue but the method itself runs in concurrent block. So, the result is random in such case. You could also serialize that using dispatch_group like this,

-(void)testSerialQueue
{
    dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);

    self.group = dispatch_group_create();

    dispatch_async(serialQueue, ^{
        [self countTo100];
    });

    dispatch_async(serialQueue, ^{
        [self countFrom200To400];

    });

    dispatch_async(serialQueue, ^{
        [self countFrom400To500];
    });
}

- (void)countTo100
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);
    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
}

- (void)countFrom200To400
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 200; i < 400; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);
    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);

}

- (void)countFrom400To500
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 400; i < 500; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);

    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);

}

Here, you will again see that it prints the log orderly. So, dispatch_group is used to serialize the concurrent operation. You might prefer to remove dispatch_async in serialQueue at this time.

Now, operation queues, lets see simple example,

-(void)testSerialQueue
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;

    NSBlockOperation *operation1 = [[NSBlockOperation alloc] init];
    NSBlockOperation *operation2 = [[NSBlockOperation alloc] init];
    NSBlockOperation *operation3 = [[NSBlockOperation alloc] init];

    [operation1 addExecutionBlock:^{
        [self countTo100];
    }];

    [operation2 addExecutionBlock:^{
        [self countFrom200To400];
    }];

    [operation3 addExecutionBlock:^{
        [self countFrom400To500];
    }];        
}

- (void)countTo100
{
    for (int i = 0; i < 100; i++) {
        NSLog(@"%d", i);
    }
}

- (void)countFrom200To400
{
    for (int i = 200; i < 400; i++) {
        NSLog(@"%d", i);
    }
}

- (void)countFrom400To500
{
    for (int i = 400; i < 500; i++) {
        NSLog(@"%d", i);
    }
}

The count methods are added to the NSOperationQueue with maxConcurrentOperation to '1' which works more like a serial queue now. Each of the method do not spawn some other queues and so, the counting executes on the same serial queue. So, they all print orderly.

Now, lets see an example which simulates your case.

-(void)testSerialQueue
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;

    NSBlockOperation *operation1 = [[NSBlockOperation alloc] init];
    NSBlockOperation *operation2 = [[NSBlockOperation alloc] init];
    NSBlockOperation *operation3 = [[NSBlockOperation alloc] init];

    [operation1 addExecutionBlock:^{
        [self countTo100];
    }];

    [operation2 addExecutionBlock:^{
        [self countFrom200To400];
    }];

    [operation3 addExecutionBlock:^{
        [self countFrom400To500];
    }];        
}

- (void)countTo100
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%d", i);
        }
    });

}

- (void)countFrom200To400
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{
        for (int i = 200; i < 400; i++) {
            NSLog(@"%d", i);
        }
    });
}

- (void)countFrom400To500
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{
        for (int i = 400; i < 500; i++) {
            NSLog(@"%d", i);
        }
    });
}

In this use case, you add the operations to a NSOperationQueue (ie. a serial queue) but again, the individual methods run in some other concurrent queues. So, the execution order is quite random. Now, you can work around this using dispatch_group as we did earlier with the serial queues.

-(void)testSerialQueue
{
    self.group = dispatch_group_create();

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;

    NSBlockOperation *operation1 = [[NSBlockOperation alloc] init];
    NSBlockOperation *operation2 = [[NSBlockOperation alloc] init];
    NSBlockOperation *operation3 = [[NSBlockOperation alloc] init];

    [operation1 addExecutionBlock:^{
        [self countTo100];
    }];

    [operation2 addExecutionBlock:^{
        [self countFrom200To400];
    }];

    [operation3 addExecutionBlock:^{
        [self countFrom400To500];
    }];        
}

- (void)countTo100
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);
    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
}

- (void)countFrom200To400
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 200; i < 400; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);
    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);

}

- (void)countFrom400To500
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 400; i < 500; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);

    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);

}

Now, you will see that the count got orderly again. It is because of the dispatch_group, which waits until each of the asynchronous task finishes.

For your particular use case, you can use loop to invoke some method, creating NSInvocationOperation like this,

-(void)testSerialQueue
{
    self.group = dispatch_group_create();

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;


    NSArray *selectors = @[@"countTo100", @"countFrom200To400", @"countFrom400To500"];

    for (NSString *selector in selectors) {
        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                selector:NSSelectorFromString(selector)
                                                                                  object:nil];
        [queue addOperation:operation];
    }

}

- (void)countTo100
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);
    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
}

- (void)countFrom200To400
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 200; i < 400; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);
    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);

}

- (void)countFrom400To500
{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(self.group);

    dispatch_async(concurrentQueue, ^{
        for (int i = 400; i < 500; i++) {
            NSLog(@"%d", i);
        }
        dispatch_group_leave(self.group);

    });
    dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);

}

Your target method is asynchronous. That means you can't use a plain block operation, you need to create a custom operation subclass so that the operation doesn't finish until your asynchronous method is finished.

Search google for examples of creating asynchronous operation subclasses for examples as there are a few things you need to organise.

Alternatively you could have something like an array of files (or whatever) and each time your async method finished check if there's anyyhing there, remove the first item and process it. This would carry on until the array is empty...

您可以使用信号量等待功能完成,如果成功,则使用参数再次调用它。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM