等待多个异步下载任务
Waiting for multiple asynchronous download tasks
我想同时下载一些文件,例如100个文件。所以我决定将我的下载线程添加到一个调度队列中,GCD 将调整同时有多少线程 运行。
这里的问题是:dispatch_async
中的块将立即完成,因为task
将在另一个线程上运行。所以,如果urls
的长度是100,它会立即创建100个线程。
var queueDownloadTask = dispatch_queue_create("downloadQueue", nil)
for url in urls {
dispatch_async(queueDownloadTask) {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let fileTransferSession = NSURLSession(configuration: config)
let task = fileTransferSession.downloadTaskWithURL(url, completionHandler: { (responseUrl, response, error) -> Void in
println("completed")
})
task.resume()
}
}
如何配置dispatch_async
中的块等待下载任务完成?我不想使用dispatch_semaphore
,因为它同时只允许运行一个下载任务。
你应该使用 dispatch_group_t
。请参考Apple documentation解决您的问题。
要扩展 Abhinav 的答案,您应该:
- 使用
dispatch_group_create()
创建群组。
- 在开始每个下载任务之前调用
dispatch_group_enter(group)
。
- 在任务的完成处理程序中调用
dispatch_group_leave(group)
。
- 然后调用
dispatch_group_notify(group, queue, ^{ ... })
入队一个block,待所有任务完成后执行。
您可以在 this post 中查看示例。
(顺便说一句,连续执行 100 dispatch_async
s 会立即创建 100 个线程是不正确的。系统仍然保留对使用多少线程来满足队列的控制。但是,您的代码不会等待任何任务在它之前完成 returns,也不会尝试在完成的多个任务之间进行同步。)
下载多个文件的工作objective-c示例
- (void) downloadFiles: (NSMutableArray *) fileArray : (NSString *)destParentDir{
dispatch_group_t serviceGroup = dispatch_group_create();
for (id fileInfo in fileArray) {
dispatch_group_enter(serviceGroup);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *fileName = [fileInfo valueForKey:@"name"];
//Create SubDirs if needed, note that you need to exclude file name for the dirsString :)
//[fileManager createDirectoryAtPath:dirsString withIntermediateDirectories:true attributes:nil error:NULL];
//Download file
NSURL *url = [NSURL URLWithString:@"YOUR_FILE_URL"];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if(urlData)
{
NSString *localPath = [NSString stringWithFormat:@"%@/%@", destParentDir, fileName];
[urlData writeToFile:localPath atomically:YES];
}
dispatch_group_leave(serviceGroup);
}
dispatch_group_notify(serviceGroup, dispatch_get_main_queue(),^{
NSLog(@"Complete files download");
});
}
在Swift4,
func executeMultiTask() {
//1. Create group
let taskGroup = DispatchGroup()
//2. Enter group
taskGroup.enter()
myTask1.execute(completeHandler: {
// ...
//3. Leave group
taskGroup.leave() //< balance with taskGroup.enter()
})
/* Add more tasks ...
//2. Enter group
taskGroup.enter()
myTask2.execute(completeHandler: {
//3. Leave group
defer {
// Use `defer` to make sure, `leave()` calls are balanced with `enter()`.
taskGroup.leave()
}
// ... more
})
*/
//4. Notify when all task completed at main thread queue.
taskGroup.notify(queue: .main) {
// All tasks are done.
// ...
}
}
创建一个DispatchGroup
。将每个下载包装在 enter
和 leave
中。向 notify
提供回调,当所有配对完成时将调用该回调。
var dispatchGroup = DispatchGroup()
for url in urls {
// ... Set up the download
dispatchGroup.enter()
let task = fileTransferSession.downloadTaskWithURL(url) { (responseUrl, response, error) -> Void in
println("Individual download complete.")
dispatchGroup.leave()
})
task.resume()
dispatchGroup.notify(queue: .main) {
println("All downloads complete.")
}
}
我想同时下载一些文件,例如100个文件。所以我决定将我的下载线程添加到一个调度队列中,GCD 将调整同时有多少线程 运行。
这里的问题是:dispatch_async
中的块将立即完成,因为task
将在另一个线程上运行。所以,如果urls
的长度是100,它会立即创建100个线程。
var queueDownloadTask = dispatch_queue_create("downloadQueue", nil)
for url in urls {
dispatch_async(queueDownloadTask) {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let fileTransferSession = NSURLSession(configuration: config)
let task = fileTransferSession.downloadTaskWithURL(url, completionHandler: { (responseUrl, response, error) -> Void in
println("completed")
})
task.resume()
}
}
如何配置dispatch_async
中的块等待下载任务完成?我不想使用dispatch_semaphore
,因为它同时只允许运行一个下载任务。
你应该使用 dispatch_group_t
。请参考Apple documentation解决您的问题。
要扩展 Abhinav 的答案,您应该:
- 使用
dispatch_group_create()
创建群组。 - 在开始每个下载任务之前调用
dispatch_group_enter(group)
。 - 在任务的完成处理程序中调用
dispatch_group_leave(group)
。 - 然后调用
dispatch_group_notify(group, queue, ^{ ... })
入队一个block,待所有任务完成后执行。
您可以在 this post 中查看示例。
(顺便说一句,连续执行 100 dispatch_async
s 会立即创建 100 个线程是不正确的。系统仍然保留对使用多少线程来满足队列的控制。但是,您的代码不会等待任何任务在它之前完成 returns,也不会尝试在完成的多个任务之间进行同步。)
下载多个文件的工作objective-c示例
- (void) downloadFiles: (NSMutableArray *) fileArray : (NSString *)destParentDir{
dispatch_group_t serviceGroup = dispatch_group_create();
for (id fileInfo in fileArray) {
dispatch_group_enter(serviceGroup);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *fileName = [fileInfo valueForKey:@"name"];
//Create SubDirs if needed, note that you need to exclude file name for the dirsString :)
//[fileManager createDirectoryAtPath:dirsString withIntermediateDirectories:true attributes:nil error:NULL];
//Download file
NSURL *url = [NSURL URLWithString:@"YOUR_FILE_URL"];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if(urlData)
{
NSString *localPath = [NSString stringWithFormat:@"%@/%@", destParentDir, fileName];
[urlData writeToFile:localPath atomically:YES];
}
dispatch_group_leave(serviceGroup);
}
dispatch_group_notify(serviceGroup, dispatch_get_main_queue(),^{
NSLog(@"Complete files download");
});
}
在Swift4,
func executeMultiTask() {
//1. Create group
let taskGroup = DispatchGroup()
//2. Enter group
taskGroup.enter()
myTask1.execute(completeHandler: {
// ...
//3. Leave group
taskGroup.leave() //< balance with taskGroup.enter()
})
/* Add more tasks ...
//2. Enter group
taskGroup.enter()
myTask2.execute(completeHandler: {
//3. Leave group
defer {
// Use `defer` to make sure, `leave()` calls are balanced with `enter()`.
taskGroup.leave()
}
// ... more
})
*/
//4. Notify when all task completed at main thread queue.
taskGroup.notify(queue: .main) {
// All tasks are done.
// ...
}
}
创建一个DispatchGroup
。将每个下载包装在 enter
和 leave
中。向 notify
提供回调,当所有配对完成时将调用该回调。
var dispatchGroup = DispatchGroup()
for url in urls {
// ... Set up the download
dispatchGroup.enter()
let task = fileTransferSession.downloadTaskWithURL(url) { (responseUrl, response, error) -> Void in
println("Individual download complete.")
dispatchGroup.leave()
})
task.resume()
dispatchGroup.notify(queue: .main) {
println("All downloads complete.")
}
}