【问题标题】:Dispatch queues: How to tell if they're running and how to stop them调度队列:如何判断它们是否正在运行以及如何停止它们
【发布时间】:2009-10-11 13:12:04
【问题描述】:

我只是在玩 GCD,我已经编写了一个玩具 CoinFlipper 应用程序。

抛硬币的方法如下:

- (void)flipCoins:(NSUInteger)nFlips{

    // Create the queues for work
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);

    // Split the number of flips into whole chunks of kChunkSize and the remainder.
    NSUInteger numberOfWholeChunks = nFlips / kChunkSize;
    NSUInteger numberOfRemainingFlips = nFlips - numberOfWholeChunks * kChunkSize;

    if (numberOfWholeChunks > 0) {
        for (NSUInteger index = 0; index < numberOfWholeChunks; index++) {
            dispatch_async(queue, ^{
                NSUInteger h = 0;
                NSUInteger t = 0;
                flipTheCoins(kChunkSize, &h, &t);
                dispatch_async(mainQueue, ^{
                    self.nHeads += h;
                    self.nTails += t;
                });
            });
        }
    }
    if (numberOfRemainingFlips > 0) {
        dispatch_async(queue, ^{
            NSUInteger h = 0;
            NSUInteger t = 0;
            flipTheCoins(numberOfRemainingFlips, &h, &t);
            dispatch_async(mainQueue, ^{
                self.nHeads += h;
                self.nTails += t;
            });
        });

    }
}

如您所见;我将翻转次数分解为大块,在后台翻转它们并更新主队列中的属性。窗口控制器正在观察属性,并使用运行结果更新 UI。

我查看了并发编程指南和 GCD 文档,虽然有办法暂停队列,但没有办法停止它们,并删除所有排队和未运行的对象。

我希望能够连接一个“停止”按钮以在翻转开始后取消翻转。使用NSOperationQueue,我可以观察operationCount 属性以了解它是否正在运行,cancelAllOperations 可以删除排队的块。

我查看了并发编程指南和 GCD 文档,虽然有办法暂停队列,但没有办法停止它们,并删除所有排队和未运行的对象。

所以:-

  1. 如何判断我添加到队列中的块是否仍在等待?
  2. 如何取消尚未运行的块?
  3. 我是 GCD 方面的新手,所以我做得对吗?

【问题讨论】:

    标签: objective-c cocoa multithreading grand-central-dispatch


    【解决方案1】:

    这是使用 GCD 编程时的一个半常见问题。

    简短的回答是 GCD 没有用于队列的取消 API。理由:

    1. 内存管理将变得更加复杂,因为给定的块可能负责释放()给定的内存分配。通过始终运行该块,GCD 确保内存管理保持简单。
    2. 实际上不可能在不破坏状态的情况下停止正在运行的块。
    3. 大多数需要取消逻辑的代码已经在私有数据结构中跟踪该状态。

    考虑到所有这些情况,编写这样的代码会更加高效和强大:

    dispatch_async(my_obj->queue, ^{
        bool done = false;
        // do_full_update() takes too long, therefore:
        while ( !my_obj->cancelled && !done ) {
            done = do_partial_update(my_obj);
        }
    });
    

    哦,要知道队列是否已经运行完所有已入队的块,您的代码可以简单地使用 同步 API 执行一个空块:

    dispatch_sync(my_obj->queue, ^{});
    

    如 cmets 中所述,了解工作何时完成的更好方法是使用调度组。将所有块分派到组,然后您可以将完成处理程序添加到组。一旦工作完成,完成块将运行。

    dispatch_group_t myGroup = dispatch_group_create();
    dispatch_group_async(myGroup, my_obj->queue, ^{
        bool done = false;
        while ( !my_obj->cancelled && !done ) {
            done = do_partial_update(my_obj);
        }
    });
    dispatch_group_notify(myGroup, my_obj->queue, ^{
        NSLog(@"Work is done!");
        dispatch_release(myGroup);
    });
    

    一旦您的所有块都完成,该组将是空的并触发通知块。从那里,您可以更新 UI 等。

    祝你好运,玩得开心!

    【讨论】:

    • 或者你可以创建一个新的dispatch_group,而不是同步调度,将所有块异步调度到同一个组,然后等待组完成。我认为这可能会在总行数中增加 3 行(1 行用于创建组,1 行用于等待,1 行用于销毁组)。
    • dispatch_sync(my_obj-&gt;queue, ^{}); 是否返回值?如何从这个执行中知道你的队列是否完成?
    • Sebby-- dispatch_sync(my_obj-&gt;queue, ^{}); 返回队列执行完所有块
    • 什么是my_obj
    【解决方案2】:

    如何判断是否正在运行

    BOOL dispatch_queue_is_empty(dispatch_queue_t queue)
    {
        dispatch_group_t group = dispatch_group_create();
    
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            dispatch_group_leave(group);
        });
    
        int64_t maxWaitTime = 0.00000005 * NSEC_PER_SEC;
        BOOL isReady = dispatch_group_wait(group, maxWaitTime) == 0;
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
            dispatch_release(group);
        });
    
        return isReady;
    }
    

    在应用中测试

    dispatch_queue_t queue = dispatch_queue_create("test", 0);
    
    NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");
    
    dispatch_async(queue, ^{
        for(int i = 0; i < 100; i++)
        {
            NSLog(@"... %i", i);
        }
    });
    
    NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");
    

    结果

    Is empty YES
    Is empty NO
    ... 0
    ... 1
    ... 2
    ... 3
    ... 4
    ... 5
    ... 6
    ... 7
    ... 8
    ... 9
    

    变量maxWaitTime 的默认值可以调整为想要的结果。

    【讨论】:

    • 从哪个函数返回0?你的意思是false?没有函数返回0
    • #hfossli 只需复制粘贴您的代码,显示如下: Is empty NO Is empty NO 0 1 2 3 4 5
    【解决方案3】:

    如果你有一个串行调度队列或一个并发调度队列,这里有一个可以做同样事情的代码。

    BOOL __block queueIsEmpty = false;
    dispatch_barrier_async (_dispatchQueue, ^{
        queueIsEmpty = true;
    });
    
    while (!queueIsEmpty) {
        int i = 0;  // NOOP instruction
    }
    
    // At this point your queue should be empty.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-24
      • 2011-01-06
      • 1970-01-01
      相关资源
      最近更新 更多