使用dispatch_async和异步请求出现问题

5

首先,dispatch_async如何确定使用哪个线程?它是随机选择吗?我需要进行解析和核心数据操作,因此不想阻塞UI线程,使用dispatch_async。但是,之后我发送了一个NSURLRequest来获取更多的数据,但是回调函数从未被调用(可能是因为该线程已经死亡)。

那么有什么好方法可以解决这个问题?同时,我不能使用

sendAsynchronousRequest:queue:completionHandler: 

由于部署的操作系统是 4,所以我现在只在内部发送请求。

dispatch_async(dispatch_get_main_queue(), ^{
});

这段代码是在dispatch_async(myQueue)块内部,用以解析并将数据保存到核心数据中。但是,我认为这种方式不太合适,我是指应该有一种方式可以使用dispatch_async,而不会终止线程,对吧?因为使用同步请求不是一个选择。

3个回答

12

那么第一个问题是,dispatch_async如何确定使用哪个线程?

在使用GCD时,您不应该考虑线程。这一切都在幕后为您处理。您应该考虑队列,并将其与要执行的块一起传递给dispatch_async。GCD队列确实类似于线程,但是您不需要担心幕后发生了什么 - GCD会为您处理所有这些。

至于您的代码,我假设它看起来类似于这样:

dispatch_async(myQueue, ^{
    // Do some stuff off the main queue

    dispatch_async(dispatch_get_main_queue(), ^{ 
        // Do something with the UI
    });
});

完全没有任何问题。您不需要担心发送到myQueue的块所在的线程可能会被终止。队列将一直存在,直到该块完成运行。在原始派发中向主队列调度是可以的 - 主队列上的工作将可以完美顺利地运行。

我认为您还在询问为什么在异步调度中使用NSURLRequest时,看不到它触发回调。这是因为它与当前运行循环紧密耦合,如果您当前在后台线程(您的myQueue将在其中运行),则该运行循环不会再次运行,直到另一个块被放入队列中。因此,您的回调永远不会运行。

我鼓励您更多地了解GCD以及异步vs同步的实际含义,因为我觉得您可能还没有完全理解。我在这里回答了一个类似的问题(链接)


那么我的问题是,如果我创建了很多带有同步网络请求的dispatch_async-s,我会用尽线程吗? - dariaa
dispatch_async 不等同于新线程。新的队列可能意味着新线程,也可能不是。跟我重复:“我不必担心线程,GCD会为我处理”。:-)。 - mattjgalloway
基本上,如果您将异步调度到相同的队列上,除非它是并行队列,否则这些任务将一个接一个地运行。有关更多信息,请参见我在答案末尾链接的帖子。 - mattjgalloway
所以,只是为了确保,调用同步请求在dispatch_async内部是完全可以的吗?(除了它不能被取消的事实) - dariaa
只要您没有将调度放到与dispatch_async相同的队列中,就没问题了,这应该是相当明显的。同步意味着对dispatch_sync的调用不会返回,直到您调度的块完成,而这个块在外部块返回之前无法运行 = 死锁。 - mattjgalloway

3
当您在块中使用sendAsynchronousRequest:...时,该块将在异步请求回调触发之前终止。
既然您已经在后台线程中,不妨使用同步方法在该线程内部获取您的数据-+[NSURLRequest sendSynchronousRequest:returningResponse:error:],甚至更简单的是 +[NSData dataWithContentsOfURL:]
您可以在在后台线程上运行的块中调用这些方法,并且在调用完成之前,该块不会继续执行。然后,您可以通过调用主队列来更新您的UI:
dispatch_async(yourQueue, ^{
    // Do some stuff

    NSData * fetchedData = [NSData dataWithContentsOfURL: someURL];

    // Do some more stuff with the data

    dispatch_async(dispatch_get_main_queue(), ^{
       // Do some stuff on the main queue
    }
});

但我担心的是(来自GCD参考)“函数在块完成之前不返回。调用此函数并针对当前队列会导致死锁。”而且这段代码将被执行多次。我会收到20个ID的响应,然后为每个ID发送一个请求,那么如果服务器不可用怎么办?线程将被阻塞...我习惯于认为使用任何同步请求都不是一个好主意。 - dariaa

0

你说得对,问题在于线程已经死了,因为NSURLConnection在其生命周期内不会让它保持活动状态。当连接返回时,连接将被丢弃。

你可以使用一些黑魔法显式地保持线程活动,并在完成后释放线程。在这种情况下,成功或出错时将_isFinishedLoading设置为true。

因此,在使用块时不关心线程并不正确,因为NSURLConnection实际上并不兼容。但是,块非常有用,因为框架根据底层硬件和可用资源为您选择最佳线程。

NSURLConnection* connection = [NSURLConnection connectionWithRequest:urlRequest delegate:self];
[connection start];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
_isFinishedLoading = NO;
while (!_isFinishedLoading)
{
    [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接