Ожидание множества задач асинхронной загрузки

Я хочу загрузить несколько файлов, например, 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, потому что он позволяет запускать одну задачу загрузки одновременно время.

Ответы

Ответ 1

Чтобы расширить ответ Abhinav, вы должны:

  • Используйте dispatch_group_create() для создания группы.
  • Вызов dispatch_group_enter(group) перед началом каждой задачи загрузки.
  • Вызов dispatch_group_leave(group) внутри обработчика завершения задачи.
  • Затем вызовите dispatch_group_notify(group, queue, ^{ ... }), чтобы поставить в очередь блок, который будет выполнен, когда все задачи будут завершены.

Вы можете увидеть пример в этом сообщении.

(Кстати, это неправда, что выполнение 100 dispatch_async в строке создаст сразу 100 потоков. Система по-прежнему сохраняет контроль над количеством потоков, которые будут использоваться для удовлетворения очереди. Однако ваш код не ждет любая из задач, которые нужно выполнить до того, как она вернется, и не попытается синхронизировать выполнение нескольких задач.)

Ответ 2

В Swift 4

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.
         // ...   
     })

}

Ответ 3

Рабочая цель - пример загрузки нескольких файлов

- (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");
    });
}

Ответ 4

Вы должны использовать dispatch_group_t. Пожалуйста, обратитесь к документации Apple, чтобы решить вашу проблему.