Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCD: How to remove waiting tasks from serial queue?

First I create a serial queue like this

static dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

then, at some unknown point in time a task gets added to the queue like this

dispatch_async(queue, ^{
    // do something, which takes some time
});

If the first task hasn't finished yet, the new task will wait until the first completes (that's of course what a serial queue is for).

But if I add 5 new tasks to the queue, while the original first one is still running, I don't want to execute new task no.1, then no.2, then no.3 and so on, but want to get rid of tasks 1 to 4 and directly start executing task no.5 after the original first task has finished.

In other words, I want to pop any waiting task (not the one that is currently running) off the queue, if I add a new one.

Is there a build in mechanism for this or do I have to implement this myself? And for the latter, how would I identify single tasks inside a queue and remove them?

like image 914
christoph Avatar asked Sep 07 '12 11:09

christoph


People also ask

Is global queue serial or concurrent?

Concurrent queues (also known as a type of global dispatch queue) execute one or more tasks concurrently, but tasks are still started in the order in which they were added to the queue.

Is Main queue a serial queue?

so the main queue is a serial queue, and [self doOne] , [self doTwo] , [self doThree] are executed sequentially in that order. Also, it has to be serial since the blocks run on the same thread. There is no way that a single thread can run multiple blocks concurrently.

What is dispatch work item?

A DispatchWorkItem encapsulates work to be performed on a dispatch queue or within a dispatch group. You can also use a work item as a DispatchSource event, registration, or cancellation handler.


2 Answers

Once a block has been submitted to a GCD dispatch queue, it will run. There is no way to cancel it. You can, as you know, implement your own mechanism to "abort" the block execution early.

An easier way to do this would be to use NSOperationQueue, as it already provides an implementation for canceling pending operations (i.e., those not yet running), and you can easily enqueue a block with the new-ish addOperationWithBlock method.

Though NSOperationQueue is implemented using GCD, I find GCD much easier to use in most cases. However, in this case, I would seriously consider using NSOperationQueue because it already handles canceling pending operations.

like image 65
Jody Hagins Avatar answered Oct 31 '22 15:10

Jody Hagins


With Davids answer getting me on track I succeeded in doing this like so

taskCounter++;
dispatch_async(queue, ^{
    if (taskCounter > 1) {
        taskCounter--;
        NSLog(@"%@", @"skip");
        return;
    }
    NSLog(@"%@", @"start");
    // do stuff
    sleep(3);
    taskCounter--;
    NSLog(@"%@", @"done");
});

taskCounter has to be either an ivar or a property (initialize it with 0). In that case it doesn't even need the __block attribute.

like image 38
christoph Avatar answered Oct 31 '22 13:10

christoph