【问题标题】:iOS GCD Sync with Async BlockiOS GCD 同步与异步块
【发布时间】:2015-04-12 17:59:05
【问题描述】:

我有一个带块的异步函数:

[self performAsyncTaskCompletion:(void(^) () )
 {
   //Do Something
 }
];

我需要以同步方式多次调用此函数。我尝试使用 GCD 队列:

dispatch_queue_t queue = dispatch_queue_create("com.MyApp.task", NULL);
for (int i = 0; i < array.count; i++)
{
   dispatch_sync(queue, ^{
     [self performAsyncTaskCompletion:(void(^) () )
      {
        //Do Something
      }
      ];
   });
}

但它不起作用,因为 dispatch_sync 只是在等待块的结束。 我如何要求它等待其块中的异步函数结束?

【问题讨论】:

  • 这些performAsyncTaskCompletion 块可以相互同时运行吗?或者你需要一个等待前一个完成。另外,我知道您要求在块的末尾等待,但这总是错误的模式。通常我们让它异步运行,但只指定一个代码块,当所有其他调度的块完成时将运行。这对你有用吗?
  • 是的,这就是我想做的。实际上,我想从网络服务器(异步)下载一堆不同的 JSON 文件。想象一下,每个 JSON 都会引导我到另一个 JSON 下载。可能我已经下载了下一个 JSON,所以我只需要执行一个同步任务来从一个数组中获取它。因此,JSON 下载应该是同步的。最后我需要一个块说“好的,一切都已下载”。我应该使用什么样的模式?
  • 您说“想象一下每个 JSON 都将我引向另一个”。你是说每一个都真正依赖于前一个吗?通常,人们希望最大限度地提高并发性,但如果每个 JSON 都需要来自前一个 JSON 的数据,那就会限制你。
  • 是的,我需要前一个来获取更多信息。我必须下载 2 JSON : B & C;但他们都将我引向另一个 JSON A 以获取更多信息。
  • 是的,无论逻辑依赖是什么,尊重它们,但在可能的范围内,尽可能同时运行。如果你真的只有三个请求,它可能只有适度的影响,但你发出的请求越多,影响就越大。

标签: ios objective-c asynchronous block grand-central-dispatch


【解决方案1】:

如果您想在完成一系列异步任务后启动某个进程,但希望允许这些任务彼此同时运行(尤其是网络请求,可以提供性能比顺序运行要好得多):

dispatch_group_t group = dispatch_group_create();

for (int i = 0; i < array.count; i++) {
    dispatch_group_enter(group);
    [self performAsyncTaskCompletion: ^{
        //Do Something
        dispatch_group_leave(group);
    }];
}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // do this when its all done
});

就个人而言,我什至可能倾向于对performAsyncTaskCompletion 进行更彻底的重构,改用异步NSOperation 子类模式。然后,您可以将这些添加到指定maxConcurrentOperationCountNSOperationQueue 中,从而实现相同的并发性,同时还控制并发度。但希望以上说明了这个想法:并发运行任务,但检测这些任务的完成而不阻塞主线程。

【讨论】:

  • 谢谢 :) performAsyncTaskCompletion 依赖于 AFNetworking,我无法真正修改它。
  • 首先,如果您使用的是AFHTTPRequestOperationManagerAFHTTPRequestOperation,那么您已经在使用基于操作队列的解决方案(然后我会为您的完成操作设置标准的NSOperation 依赖项)。即使您使用AFHTTPSessionManager,您仍然可以将它们包装在您自己的NSOperation 子类中。这些都不需要修改 AFNetworking,而只需 performAsyncTaskCompletion。无论如何,通知方法仍然有效。但是,不要同步执行这些操作。
【解决方案2】:

您可以将dispatch_asyncsemaphores 一起使用:

例子:

- (void)performAsyncTaskCompletion:(void (^)())block {
    if (block) {
        block();
    }
}

- (void)runChainOfOperations {
    static dispatch_once_t onceToken;
    static dispatch_semaphore_t semaphore;
    static dispatch_queue_t queue;

    dispatch_once(&onceToken, ^{
        semaphore = dispatch_semaphore_create(1);
        queue = dispatch_queue_create("com.example.MyApp", NULL);
    });

    NSArray *array = @[@1, @2, @3, @4, @5];

    static long counter = 0;
    for (int i = 0; i < array.count; i++) {
        dispatch_async(queue, ^{
            dispatch_semaphore_wait(semaphore,  DISPATCH_TIME_FOREVER);

            [self performAsyncTaskCompletion:^{
                sleep(10);

                dispatch_async(dispatch_get_main_queue(), ^{
                     NSLog(@"%ld", counter++);
                });

                dispatch_semaphore_signal(semaphore);
             }];  
        });
    }
}

控制台输出:

2015-04-12 21:28:06.047 HKTest[9497:1136830] 0
2015-04-12 21:28:16.023 HKTest[9497:1136830] 1
2015-04-12 21:28:26.025 HKTest[9497:1136830] 2
2015-04-12 21:28:36.029 HKTest[9497:1136830] 3
2015-04-12 21:28:46.031 HKTest[9497:1136830] 4

【讨论】:

  • 非常好的答案!你能告诉我在异步任务完成时如何从 runChainOfOperations 回调一个块吗?
  • 你应该在 for 循环之后再添加一个 dispatch_async 块。
  • 我认为您的示例给出了使用“信号量”的错误方法,在您的示例中没有使用“dispatch_semaphore_wait”
猜你喜欢
  • 2014-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多