1

我正在构建一个 iOS 应用程序,它在后台线程上做了一些繁重的工作。

我使用创建我的线程

  dispatch_queue_t backgroundQueue;
  backgroundQueue =  dispatch_queue_create("MyQueue", NULL);

然后使用以下命令将其放入 GCD:

   dispatch_async(backgroundQueue, ^
  {

   //some heavy operations here
   //each of them might run for >1 sec

  }

我只想要队列的顺序执行,只要它不阻塞主线程。如果在 20ms 内从方法 3、方法 2 和方法 1 调用此块......它们必须按照 3 -> 2 -> 1 的顺序执行,因为每个方法的输出都会被下一个用完。

我是 GCD 的新手,天真地认为调度队列会使用 FIFO 队列来执行顺序调用。然而,在其当前的实现中,它远非顺序。

我尝试使用 NSLock,但它们也没有帮助。

我想知道在 GCD 中强制执行顺序执行的正确机制。

编辑1:

我声明一个全局队列:

 dispatch_queue_t backgroundQueue ;

并在 viewDidLoad() 中启动它:

 backgroundQueue = dispatch_queue_create("MyQueue", DISPATCH_QUEUE_SERIAL);
 //using DISPATCH_QUEUE_SERIAL was a new idea, I also tried using NULL

我正在使用 GCD 基本上调用另一个类中的方法:

 -(void)doAction()
  {

   dispatch_async(backgroundQueue, ^
        {
                   MyOtherClass *obj = [[MyOtherClass alloc] init];
                   [obj heavyMethod1:  param1 : param2];
                   [obj release];
               });

        }

 -(void)doAnotherAction()
  {

   dispatch_async(backgroundQueue, ^
        {
                   MyOtherClass *obj = [[MyOtherClass alloc] init];
                   [obj heavyMethod2:  param3 : param4];
                   [obj release];
               });

        }

现在,doAction 和 doAnotherAction 方法被许多其他方法调用,具体取决于场景。

我在这里看到的是 - 如果我按顺序调用这些方法 :: doAction -> doAction -> doAnotherAction -> doAction

...我按如下顺序获得输出: :: doAction -> doAnotherAction -> doAction -> doAction

如何保持其调用顺序并同时保持其连续性?

PS: - 如果我删除 GCD 并允许在主线程中进行,理论上它运行良好 - 在模拟器中。(它在 iPhone 中崩溃。)

4

3 回答 3

5

这个线程似乎有很多混乱 - 为了尝试减轻这种情况,让我首先陈述一些关于 GCD 的基本事实:

  1. 串行队列总是串行和非并发地执行项目(项目在同一个队列中)。甚至没有办法让 GCD 在串行队列中“从任何它想要的地方获取项目”,所以那里似乎有些混乱。

  2. 并发队列可以在任意数量的并发线程上以任意顺序执行提交给它们的块。除非您将队列创建为并发队列或使用全局并发队列之一,否则不会出现此行为。

  3. dispatch_async 总是立即返回,无论它发布工作的队列类型是什么,因此如果您有多个方法将工作发布到同一个串行队列并且这些方法是从多个地方调用的,那么很有可能以不可预测的顺序交错工作在你的代码中!例如,您有 doAction 和 doAnotherAction 方法,但不清楚它们在哪里被调用,并且由于每个方法都可能或多或少地立即返回,您可以很容易地看到一个 doAction/doAction/doAnotherAction 提交链。

如果希望同时做多件事情,但每件事情都以可预测的顺序,你应该为每件“事情”创建一个串行队列——这就是通常保持对关键资源的独占访问(没有锁)的方式——每个资源都有自己的关联串行队列。

这更有意义吗?

于 2013-05-16T16:21:45.220 回答
1

文档提供了以下答案:

当您希望任务以特定顺序执行时,串行队列很有用。串行队列一次只执行一个任务,并且总是从队列的头部拉取任务。您可以使用串行队列而不是锁来保护共享资源或可变数据结构。与锁不同,串行队列确保任务以可预测的顺序执行。

...

    dispatch_queue_t queue;
    queue = dispatch_queue_create("com.example.MyQueue", NULL);

所以原则上你做的是正确的事情。您可能在单独的线程上分离每个块。那是行不通的,因为每个单独的串行队列将同时运行。只需将所有工作提交到同一个队列中,您就应该很成功了。

编辑我:

好吧,就GCD而言,我很难过。也许你可以试试NSOperation类的家庭?您可以创建 NSBlockOperation 并将其添加到 NSOperationQueue,如本文档中所述。如果您创建第二个(或第三个等)NSBlockOperation,您可以这样做[anotherBlock addDependency:[yourNSOperationQueue.operations lastObject]]并且应该anotherBlock等待您最后添加到的操作yourNSOperationQueue

供参考(来源):

要在两个操作对象之间建立依赖关系,可以使用 NSOperation 的 addDependency: 方法。此方法创建从当前操作对象到您指定为参数的目标操作的单向依赖关系。这种依赖意味着当前对象在目标对象完成执行之前无法开始执行。

于 2013-05-16T09:08:28.280 回答
1

你的代码是正确的。看例子:

dispatch_queue_t backgroundQueue;
backgroundQueue =  dispatch_queue_create("SerialQueue", NULL);

for (int i = 0; i < 10; i++){
    dispatch_async(backgroundQueue, ^{
        int sleepTime = rand()%5;
        sleep(sleepTime);
        NSLog(@"SerialQueue: task %d. sleepTime %d",i,sleepTime);
    });
}

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 10; i++){
    dispatch_async(aQueue, ^{
        int sleepTime = rand()%5;
        sleep(sleepTime);
        NSLog(@"ConcurrentQueue task %d. sleepTime %d",i,sleepTime);
    });
}

编辑 什么是“重”方法?是同步的还是异步的?试试这样的例子:

- (void)makeTest{
    [self doActionId: 1];
    [self doActionId: 2];
    [self doAnotherActionId: 3];
    [self doActionId: 4];
}


- (void)doActionId: (NSInteger) id{
    dispatch_async(backgroundQueue, ^{
        int sleepTime = rand()%5;
        sleep(sleepTime);
        NSLog(@"doAction - %d.Sleep time %d", id, sleepTime);
    });
}
- (void)doAnotherActionId:(NSInteger) id{
    dispatch_async(backgroundQueue, ^{
        int sleepTime = rand()%5;
        sleep(sleepTime);
        NSLog(@"doAnotherAction %d. Sleep time %d", id, sleepTime);
    });
}
于 2013-05-16T09:02:29.650 回答