等待多个异步下载任务

时间:2015-09-18 02:12:06

标签: ios multithreading swift grand-central-dispatch

我想同时下载一些文件,例如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,因为它只允许运行一次下载同时完成任务。

5 个答案:

答案 0 :(得分:29)

在Swift3中,

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
     taskGroup.notify(queue: DispatchQueue.main, work: DispatchWorkItem(block: { 
         // All tasks are done.
         // ...   
     })

}

答案 1 :(得分:28)

要扩展Abhinav的答案,你应该:

  1. 使用dispatch_group_create()创建群组。
  2. 在开始每个下载任务之前调用dispatch_group_enter(group)
  3. 在任务的完成处理程序中调用dispatch_group_leave(group)
  4. 然后调用dispatch_group_notify(group, queue, ^{ ... })以排队将在所有任务完成后执行的块。
  5. 您可以在this post中看到一个示例。

    (顺便说一句,连续执行100 dispatch_async并不会立即创建100个线程。系统仍然可以控制用于满足队列的线程数。但是,您的代码确实如此。不等待任何任务在返回之前完成,也不会尝试在完成的多个任务之间进行同步。)

答案 2 :(得分:3)

下载多个文件的工作目标-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");
    });
}

答案 3 :(得分:2)

您应该使用dispatch_group_t。请参阅Apple documentation以解决您的问题。

答案 4 :(得分:0)

创建一个 DispatchGroup。将每次下载都包装在 enterleave 中。提供对 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.")
    }
}