12

首先我创建一个这样的串行队列

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

然后,在某个未知的时间点,一个任务像这样被添加到队列中

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

如果第一个任务尚未完成,则新任务将等到第一个任务完成(这当然是串行队列的用途)。

但是如果我将 5 个新任务添加到队列中,而原来的第一个任务仍在运行,我不想执行新任务 no.1,然后是 no.2,然后是 no.3 等等,而是想得到去掉task 1到task 4,在原来的第一个task完成后直接开始执行task no.5。

换句话说,如果我添加一个新任务,我想从队列中弹出任何等待任务(不是当前正在运行的任务)。

是否有内置机制,还是我必须自己实现?对于后者,我将如何识别队列中的单个任务并删除它们?

4

3 回答 3

19

一旦一个块被提交到 GCD 调度队列,它就会运行。没有办法取消它。如您所知,您可以实现自己的机制来提前“中止”块执行。

一个更简单的方法是使用NSOperationQueue,因为它已经提供了一个用于取消挂起操作(即那些尚未运行的操作)的实现,并且您可以使用 new-ishaddOperationWithBlock方法轻松地将块排队。

虽然NSOperationQueue是使用 GCD 实现的,但我发现 GCD 在大多数情况下更容易使用。但是,在这种情况下,我会认真考虑使用NSOperationQueue,因为它已经处理取消挂起的操作。

于 2012-09-07T14:39:44.967 回答
5

随着戴维斯的回答让我走上正轨,我成功地做到了这一点

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

taskCounter必须是 ivar 或属性(用 初始化0)。在这种情况下,它甚至不需要该__block属性。

于 2012-09-07T12:26:53.487 回答
4

你处理这个问题的方法是使用一个 ivar 来指示他们应该返回的排队块:

^{
  if(!canceled) {
    ... do work
  }
}

您也不需要使用简单的布尔值 - 您可以使其更复杂 - 但总体思路是在执行任何操作之前使用块查询的一个或多个 ivars。

我使用这种技术(但没有发明它)取得了巨大的成功。

于 2012-09-07T11:28:15.327 回答