Swift,dispatch_group_wait没有等待

Jim*_*alo 7 multithreading grand-central-dispatch ios swift

我正在尝试使用宏中央调度等待文件完成下载后再继续.这个问题是从这个问题衍生出来的:Swift(iOS),等待所有图像在返回之前完成下载.

我只是试图找出如何让dispatch_group_wait(或类似)实际等待,而不是在下载完成之前继续.请注意,如果我使用NSThread.sleepForTimeInterval而不是调用downloadImage,它等待就好了.

我错过了什么?

class ImageDownloader {

    var updateResult = AdUpdateResult()

    private let fileManager = NSFileManager.defaultManager()
    private let imageDirectoryURL = NSURL(fileURLWithPath: Settings.adDirectory, isDirectory: true)

    private let group = dispatch_group_create()
    private let downloadQueue = dispatch_queue_create("com.acme.downloader", DISPATCH_QUEUE_SERIAL)

    func downloadImages(imageFilesOnServer: [AdFileInfo]) {

        dispatch_group_async(group, downloadQueue) {

            for serverFile in imageFilesOnServer {
                print("Start downloading \(serverFile.fileName)")
                //NSThread.sleepForTimeInterval(3) // Using a sleep instead of calling downloadImage makes the dispatch_group_wait below work
                self.downloadImage(serverFile)
            }
        }
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // This does not wait for downloads to finish.  Why?

        print("All Done!") // It gets here too early!
    }

    private func downloadImage(serverFile: AdFileInfo) {

        let destinationPath = imageDirectoryURL.URLByAppendingPathComponent(serverFile.fileName)

        Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath }
        .response { _, _, _, error in
            if let error = error {
                print("Error downloading \(serverFile.fileName): \(error)")
            } else {
                self.updateResult.filesDownloaded++
                print("Done downloading \(serverFile.fileName)")
            }
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

注意:这些下载是对HTTP POST请求的响应,我使用的是不支持异步操作的HTTP服务器(Swifter),因此我需要等待完整下载完成才能返回响应(请参阅原始问题引用上面有更多细节).

Rob*_*Rob 11

dispatch_group_async用于调用本身异步的方法时,只要所有异步任务都已启动,该组就会完成,但不会等待它们完成.相反,您可以dispatch_group_enter在进行异步调用之前手动调用,然后dispatch_group_leave在异步调用完成时调用.然后dispatch_group_wait现在将按预期运行.

但是,要实现此目的,首先要更改downloadImage为包含完成处理程序参数:

private func downloadImage(serverFile: AdFileInfo, completionHandler: (NSError?)->()) {
    let destinationPath = imageDirectoryURL.URLByAppendingPathComponent(serverFile.fileName)

    Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath }
        .response { _, _, _, error in
            if let error = error {
                print("Error downloading \(serverFile.fileName): \(error)")
            } else {
                print("Done downloading \(serverFile.fileName)")
            }
            completionHandler(error)
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经做了一个传递错误代码的完成处理程序.根据你的需要调整,但希望它能说明这个想法.

但是,提供完成处理程序后,现在,当您执行下载时,您可以创建一个组,在启动每次下载之前"输入"该组,在异步调用完成处理程序时"离开"该组.

dispatch_group_wait如果你不小心可以死锁,如果从主线程完成,可以阻止UI等等.更好的是,你可以dispatch_group_notify用来实现所需的行为.

func downloadImages(_ imageFilesOnServer: [AdFileInfo], completionHandler: @escaping (Int) -> ()) {
    let group = DispatchGroup()

    var downloaded = 0

    group.notify(queue: .main) {
        completionHandler(downloaded)
    }

    for serverFile in imageFilesOnServer {
        group.enter()

        print("Start downloading \(serverFile.fileName)")

        downloadImage(serverFile) { error in
            defer { group.leave() }

            if error == nil {
                downloaded += 1
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你会这样称呼它:

downloadImages(arrayOfAdFileInfo) { downloaded in
    // initiate whatever you want when the downloads are done

    print("All Done! \(downloaded) downloaded successfully.")
}

// but don't do anything contingent upon the downloading of the images here
Run Code Online (Sandbox Code Playgroud)

对于Swift 2和Alamofire 3的答案,请参阅此答案的上一版本.


Cod*_*ide 7

在Swift 3 ......

let dispatchGroup = DispatchGroup()

dispatchGroup.enter()
// do something, including background threads

dispatchGroup.leave()

dispatchGroup.notify(queue: DispatchQueue.main) {
    // completion code
}
Run Code Online (Sandbox Code Playgroud)

https://developer.apple.com/reference/dispatch/dispatchgroup