1

我有一个异步运行并通过完成处理程序返回结果的方法。方法调用本身“立即”返回,但完成处理程序可能需要一些时间才能被调用。

我想序列化对这个方法的调用,这样如果完成处理程序在上一次调用中没有被处理,它就不会运行两次。

现在我正在使用一个标志

-(void)myAsyncMethod:(void(^)(NSError *error))block
{
  if(_isRunning) 
  {
     return; // would like to queue the call
  }
  _isRunning = YES;
  // do some long running async work which themselves can have completion handlers and run asynchronously

  // look for ever place with a call to "block"

  _isRunning = NO;
  block(error); 
}

有没有办法在不使用 dispatch_sync、async 等方法调用中的标志的情况下做到这一点?

4

2 回答 2

4

您只需创建一次串行调度队列:

self.serialQueue = dispatch_queue_create("identifier", DISPATCH_QUEUE_SERIAL);

并在该队列上提交一个异步执行块:

- (void)myAsyncMethod:(void(^)(NSError *error))block
{
    dispatch_async(self.serialQueue, ^{
        NSError *error;
        // long running task ...
        block(error);
    });
}

更新:再次阅读您的问题后,我意识到如果“长时间运行的任务”本身与完成处理程序异步工作,则上述方法不起作用。

为了解决这种情况,您可以使用“调度组”:

self.group = dispatch_group_create();

-(void)myAsyncMethod:(void(^)(NSError *error))block
{
    dispatch_async(self.serialQueue, ^{
        // Start job:
        dispatch_group_enter(self.group);
        NSError *error;

        [self longRunningTaskWithCompletionHandler:^(void){
            block(error);
            // Signal that job is done:
            dispatch_group_leave(self.group);
        }];

        // Wait until job is done:
        dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
    });
}

(或者,可以使用“调度信号量”。)

于 2013-09-10T12:49:37.890 回答
0

其实你做的已经是最简单的方法了。您不需要任何额外的队列或组或信号量,或者,除了您正在做的事情之外的任何东西!

为了回答隐含的问题,检查内部状态标志并没有错——事实上,取消就是这样工作的。如果您希望能够在将某些内容放入队列后取消它,您最好有一个可以调用的方法(立即调用,而不是入队),该方法设置一个内部“is_cancelled”标志,然后在开始时检查它工作块/功能,并选择直接返回(如果已设置)。取消没有灵丹妙药,就像“如果我的工作还没有完成,请不要运行我两次”没有灵丹妙药——你所做的基本上等同于取消,你只是“取消”后续呼叫,而第一个呼叫仍在工作。

于 2013-09-12T06:31:38.570 回答