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