使用 Dispatch Group 时异步函数需要很长时间
Async Functions taking long when using Dispatch Group
我正在使用 Firebase Storage 来存储我的应用程序用户的个人资料照片,其中最多可以有六张。因此,每当用户保存他们的图像时,我都会创建一个 DispatchGroup
并同时删除 Firebase 存储中所有以前关联的 photoUrls,并将新图像上传到 Firebase 存储。 Firebase 数据库中的用户对象也已更新。我想这是我实现方式的问题 DispatchGroup
,所以我将提供我如何使用它的伪代码:
//PSEUDOCODE
func buttonPressed() {
let group = DispatchGroup()
for url in oldUrl {
group.enter()
asyncFuncThatDeletesImage(at: url) {
group.leave()
}
}
for image in newImages {
group.enter()
asyncFuncThatUploadsNewImage(image) {
group.leave()
}
}
group.notify(queue: .main) {
//continue
}
}
我的异步函数(asyncFuncThatDeletesImage
和 asyncFuncThatUploadsNewImage
) 单独执行最多需要几秒钟。但是,当使用我的 DispatchGroup
时,.notify
直到大约 2 分钟后才被调用 。
这里是所有感兴趣的人的完整代码:
@IBAction func saveButtonPressed(_ sender: Any) {
let oldUrls = parentVC.photoUrls
activityIndicator.isHidden = false
activityIndicator.startAnimating()
let photoUploader = PhotoUploader()
var data = [Data]()
var urls: [Int:String] = [:]
let group = DispatchGroup()
for oldUrl in oldUrls {
group.enter()
let storageRef = Storage.storage().reference(forURL: oldUrl)
//Removes image from storage
storageRef.delete { error in
if let error = error {
print(error)
group.leave()
} else {
// File deleted successfully
print("File deleted, took \(Date().timeIntervalSince1970 - startTime) seconds")
group.leave()
}
}
}
for (index,image) in imagesArray.enumerated() {
group.enter()
let imageData = image.jpegData(compressionQuality: 1)!
data.append(imageData)
photoUploader.upload(image: image, to: "Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(index)", completion: {(url, error) in
if error == nil {
urls[index] = url
group.leave()
print("Image uploaded, took \(Date().timeIntervalSince1970 - startTime) seconds")
} else {
print(error?.localizedDescription)
group.leave()
}
})
}
for i in imagesArray.count...6 {
Database.database().reference().child("Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(i)").removeValue()
}
group.notify(queue: .main) {
//Do whatever I need to do
}
这是我的函数 PhotoUploader
class:
func upload(image: UIImage, to firebasePath: String, completion: @escaping (String?, Error?) -> Void) {
//... creates data from the image...
var data = NSData()
// data = UIImageJPEGRepresentation(image, 0.8)! as NSData
data = image.jpegData(compressionQuality: 0.8)! as! NSData
//...sets the upload path
let filePath = "\(Auth.auth().currentUser!.uid)\(String.randomStringWithLength(len: 20))" // path where you wanted to store img in storage
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
let storageRef = Storage.storage().reference().child(filePath)
storageRef.putData(data as Data, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
completion(nil, error)
return
} else {
storageRef.downloadURL(completion: { (url, error) in
//Returns the url string to the newly uploaded image so that we can set the user's photoURL database property to this string
Database.database().reference().child(firebasePath).setValue(url!.absoluteString)
completion(url!.absoluteString, nil)
})
}
}
}
可能是您将 notify
放入了 .main
队列。或者你的 async
函数不应该在 .main
队列中。无论哪种方式,我都会创建一个独立的 .concurrent
队列,并将您的 async
调用和 notify
放在上面。即
let customQueue = DispatchQueue(label: "customQueue", attributes: .concurrent)
...
func asyncFuncThatUploadsNewImage(image){
customQueue.async{ ... }
}
func asyncFuncThatDeletesImage(image){
customQueue.async{ ... }
}
...
group.notify(queue: customQueue) {
//continue
}
在我看来你正确地使用了 DispatchGroup。
将事物放在主队列中不同于自定义队列有几个原因,其中之一是它是串行队列,而不是并发队列,而且(我认为)其他事物可能是 运行 在您不知道的主队列上,这使您必须做的推理变得复杂。
从 OP 编辑
建议的编辑工作,但我不完全确定为什么。此外,在 group.notify(queue:)
方法中,即使我通过了自定义队列,我也不得不将包含的代码包装在主线程上。这很有趣,但这是有效的代码:
@IBAction func saveButtonPressed(_ sender: Any) {
let oldUrls = parentVC.photoUrls
activityIndicator.isHidden = false
activityIndicator.startAnimating()
let photoUploader = PhotoUploader()
var data = [Data]()
var urls: [Int:String] = [:]
//Custom queue
let customQueue = DispatchQueue(label: "customQueue", attributes: .concurrent)
let group = DispatchGroup()
for oldUrl in oldUrls {
group.enter()
customQueue.async {
let storageRef = Storage.storage().reference(forURL: oldUrl)
//Removes image from storage
storageRef.delete { error in
if let error = error {
print(error)
group.leave()
} else {
// File deleted successfully
print("File deleted, took \(Date().timeIntervalSince1970 - startTime) seconds")
group.leave()
}
}
}
}
for (index,image) in imagesArray.enumerated() {
group.enter()
customQueue.async {
let imageData = image.jpegData(compressionQuality: 1)!
data.append(imageData)
photoUploader.upload(image: image, to: "Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(index)", completion: {(url, error) in
if error == nil {
urls[index] = url
group.leave()
print("Image uploaded, took \(Date().timeIntervalSince1970 - startTime) seconds")
} else {
print(error?.localizedDescription)
group.leave()
}
})
}
}
for i in imagesArray.count...6 {
Database.database().reference().child("Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(i)").removeValue()
}
group.notify(queue: customQueue) {
DispatchQueue.main.async {
//Do whatever I need to do
}
}
我正在使用 Firebase Storage 来存储我的应用程序用户的个人资料照片,其中最多可以有六张。因此,每当用户保存他们的图像时,我都会创建一个 DispatchGroup
并同时删除 Firebase 存储中所有以前关联的 photoUrls,并将新图像上传到 Firebase 存储。 Firebase 数据库中的用户对象也已更新。我想这是我实现方式的问题 DispatchGroup
,所以我将提供我如何使用它的伪代码:
//PSEUDOCODE
func buttonPressed() {
let group = DispatchGroup()
for url in oldUrl {
group.enter()
asyncFuncThatDeletesImage(at: url) {
group.leave()
}
}
for image in newImages {
group.enter()
asyncFuncThatUploadsNewImage(image) {
group.leave()
}
}
group.notify(queue: .main) {
//continue
}
}
我的异步函数(asyncFuncThatDeletesImage
和 asyncFuncThatUploadsNewImage
) 单独执行最多需要几秒钟。但是,当使用我的 DispatchGroup
时,.notify
直到大约 2 分钟后才被调用 。
这里是所有感兴趣的人的完整代码:
@IBAction func saveButtonPressed(_ sender: Any) {
let oldUrls = parentVC.photoUrls
activityIndicator.isHidden = false
activityIndicator.startAnimating()
let photoUploader = PhotoUploader()
var data = [Data]()
var urls: [Int:String] = [:]
let group = DispatchGroup()
for oldUrl in oldUrls {
group.enter()
let storageRef = Storage.storage().reference(forURL: oldUrl)
//Removes image from storage
storageRef.delete { error in
if let error = error {
print(error)
group.leave()
} else {
// File deleted successfully
print("File deleted, took \(Date().timeIntervalSince1970 - startTime) seconds")
group.leave()
}
}
}
for (index,image) in imagesArray.enumerated() {
group.enter()
let imageData = image.jpegData(compressionQuality: 1)!
data.append(imageData)
photoUploader.upload(image: image, to: "Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(index)", completion: {(url, error) in
if error == nil {
urls[index] = url
group.leave()
print("Image uploaded, took \(Date().timeIntervalSince1970 - startTime) seconds")
} else {
print(error?.localizedDescription)
group.leave()
}
})
}
for i in imagesArray.count...6 {
Database.database().reference().child("Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(i)").removeValue()
}
group.notify(queue: .main) {
//Do whatever I need to do
}
这是我的函数 PhotoUploader
class:
func upload(image: UIImage, to firebasePath: String, completion: @escaping (String?, Error?) -> Void) {
//... creates data from the image...
var data = NSData()
// data = UIImageJPEGRepresentation(image, 0.8)! as NSData
data = image.jpegData(compressionQuality: 0.8)! as! NSData
//...sets the upload path
let filePath = "\(Auth.auth().currentUser!.uid)\(String.randomStringWithLength(len: 20))" // path where you wanted to store img in storage
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
let storageRef = Storage.storage().reference().child(filePath)
storageRef.putData(data as Data, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
completion(nil, error)
return
} else {
storageRef.downloadURL(completion: { (url, error) in
//Returns the url string to the newly uploaded image so that we can set the user's photoURL database property to this string
Database.database().reference().child(firebasePath).setValue(url!.absoluteString)
completion(url!.absoluteString, nil)
})
}
}
}
可能是您将 notify
放入了 .main
队列。或者你的 async
函数不应该在 .main
队列中。无论哪种方式,我都会创建一个独立的 .concurrent
队列,并将您的 async
调用和 notify
放在上面。即
let customQueue = DispatchQueue(label: "customQueue", attributes: .concurrent)
...
func asyncFuncThatUploadsNewImage(image){
customQueue.async{ ... }
}
func asyncFuncThatDeletesImage(image){
customQueue.async{ ... }
}
...
group.notify(queue: customQueue) {
//continue
}
在我看来你正确地使用了 DispatchGroup。
将事物放在主队列中不同于自定义队列有几个原因,其中之一是它是串行队列,而不是并发队列,而且(我认为)其他事物可能是 运行 在您不知道的主队列上,这使您必须做的推理变得复杂。
从 OP 编辑
建议的编辑工作,但我不完全确定为什么。此外,在 group.notify(queue:)
方法中,即使我通过了自定义队列,我也不得不将包含的代码包装在主线程上。这很有趣,但这是有效的代码:
@IBAction func saveButtonPressed(_ sender: Any) {
let oldUrls = parentVC.photoUrls
activityIndicator.isHidden = false
activityIndicator.startAnimating()
let photoUploader = PhotoUploader()
var data = [Data]()
var urls: [Int:String] = [:]
//Custom queue
let customQueue = DispatchQueue(label: "customQueue", attributes: .concurrent)
let group = DispatchGroup()
for oldUrl in oldUrls {
group.enter()
customQueue.async {
let storageRef = Storage.storage().reference(forURL: oldUrl)
//Removes image from storage
storageRef.delete { error in
if let error = error {
print(error)
group.leave()
} else {
// File deleted successfully
print("File deleted, took \(Date().timeIntervalSince1970 - startTime) seconds")
group.leave()
}
}
}
}
for (index,image) in imagesArray.enumerated() {
group.enter()
customQueue.async {
let imageData = image.jpegData(compressionQuality: 1)!
data.append(imageData)
photoUploader.upload(image: image, to: "Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(index)", completion: {(url, error) in
if error == nil {
urls[index] = url
group.leave()
print("Image uploaded, took \(Date().timeIntervalSince1970 - startTime) seconds")
} else {
print(error?.localizedDescription)
group.leave()
}
})
}
}
for i in imagesArray.count...6 {
Database.database().reference().child("Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(i)").removeValue()
}
group.notify(queue: customQueue) {
DispatchQueue.main.async {
//Do whatever I need to do
}
}