dispatch group and Threading swift (Async Await)



所以我在练习线程,我有一个要求。我想等待第一个api调用完成,然后执行第二个调用,(就像等待异步一样(如果第一个调用没有完成,我想在那里停止代码,直到它完成。

所以,我写了这个。告诉我它是否正确,或者是否有更好的方法来完成

func myFunction() {
var a: Int?
let group = DispatchGroup()
group.enter()
DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
a = 1
print("First")
group.leave()
}
group.wait()
group.enter()
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
a = 3
print("Second")
group.leave()
}
// wait ...
group.wait()
group.notify(queue: .main) {
print(a) // y
}
}

Swift 5.5和Xcode 13.0带来了async/await,所以你可以这样做:


import Foundation
print("Hello, World!")
// Returns a 1 after a 3-second delay
func giveMeAOne() async -> Int {
Thread.sleep(forTimeInterval: 3.0)
return 1
}
// Returns a 5 after a 3-second delay
func giveMeAFive() async -> Int {
Thread.sleep(forTimeInterval: 3.0)
return 5
}
func myFunction() async {
var a: Int = 0
print(a)
a += await giveMeAOne()
print(a)
a += await giveMeAFive()
print(a)
}
async {
await myFunction()
}
sleep(7) // added at the end so that process doesn't exit right away

请注意,上面是作为Xcode中的macOS命令行工具构建的,而不是Swift Playgrounds。不幸的是,截至Xcode 13.0 beta 1,Swift Playgrounds在大量异步/等待内容上失败了。

上面的代码按顺序运行它们,并对myFunction进行了大量清理。请注意,这并没有引入任何并发giveMeAOne((giveMeAFive((按顺序运行。

如果你想同时运行4个函数调用,你可以这样做:

/// Returns a random integer from 1 through 10, after a 6-second delay
func randomSmallInt() async -> Int {
Thread.sleep(forTimeInterval: 6)
return Int.random(in: 1 ... 10)
}
/// Fetches 4 random integers, summing them
func myFunction() async {
// print timestamp
print(Date())
async let a = randomSmallInt()
async let b = randomSmallInt()
async let c = randomSmallInt()
async let d = randomSmallInt()

// This awaits for a, b, c, and d to all complete.
// You can only await a value once.
let numbersToAdd = await [a, b, c, d]
var sum = 0
for number in numbersToAdd {
print("adding (number)")
sum += number
}
print("Sum = (sum)")

// print another timestamp, so you can see that the async let statements ran
// concurrently, rather than sequentially
print(Date())
}
async {
await myFunction()
}
// make sure we have time to finish before process exits
Thread.sleep(forTimeInterval: 10)

当您运行此程序时,您可以从时间戳中看到,总执行时间只花了大约6秒——因为所有async let ...语句都是并发执行的。

如果您有固定数量的东西需要异步调用,这将非常有效。但是,如果您想进行100次异步函数调用,也许是加载图像之类的,该怎么办?

你可能认为你可以做这样的事情,这会起作用,但会同步运行所有——可能不是你想要的:

// Don't do this -- these all run sequentially
var sum = 0
for _ in 0 ..< 100 {
async let value = randomSmallishInt()
let number = await value
print("Adding (number)")
sum += number
}

如果要执行任意未知数量的异步操作,最好使用taskGroup。为了使事情更清楚,这段代码比它需要的要详细一些注意:Xcode 13.0测试版在print语句输出方面遇到了困难,缺少了大部分内容。不管怎样,它在这里:

/// Returns a random integer from 1 through 10
func randomSmallInt() async -> Int {
Thread.sleep(forTimeInterval: 6)
return Int.random(in: 1 ... 10)
}
func myFunction_arbitrary_repetitions_asynchronously() async {
// print timestamp
print(Date())

// This is pretty cool -- 
// `withTaskGroup` creates a context for executing a group of async tasks
// In this case, `Int.self` specifies that each individual task will return
// an Int.  `[Int].self` specifies that the entire group will return an array of Ints:
let numbers = await withTaskGroup(of: Int.self, returning: [Int].self) { group in
// Repeat 100 times
for _ in 0 ..< 100 {
// Run the next thing asynchronously
group.async {
return await randomSmallInt()
}
}

// Iterate through results
var result: [Int] = []
for await individualResult in group {
result.append(individualResult)
}
return result
}

var sum = 0
for number in numbers {
print("Adding (number)")
sum += number
}
print("Sum = (sum)")
// print another timestamp, so you can see that the async let statements ran
// concurrently, rather than sequentially
print(Date())
}
async { await myFunction_arbitrary_repetitions_asynchronously() }
Thread.sleep(10)

你可以大大缩短:

func myFunction_arbitrary_repetitions_asynchronously_short() async {
// print timestamp
print(Date())

// In this case, the taskGroup just returns the sum
let sum = await withTaskGroup(of: Int.self, returning: Int.self) { group in
// Repeat 100 times
for _ in 0 ..< 100 {
// Run the next thing asynchronously
group.async {
return await randomSmallInt()
}
}

// Iterate through results
var sum = 0
for await individualResult in group {
print("Adding (individualResult)")
sum += individualResult
}
return sum
}
print("Sum = (sum)")
// print another timestamp, so you can see that the async let statements ran
// concurrently, rather than sequentially
print(Date())
}
async { await myFunction_arbitrary_repetitions_asynchronously_short() }

重要提示:Xcode 13.0测试版1在处理此代码时会使变得非常不稳定。特别是,调试器喜欢在进程尚未完成时分离。例如,许多print语句的输出都不会出现,并且使用断点对异步代码来说很困难。构建一个命令行工具,然后运行生成的可执行文件(位于~/Library/Developer/Xcode/DerivedData//Build/Products/Debug中(,至少会显示打印语句的所有输出。

不要等待。

在第一个的完成处理程序中运行第二个API调用

func myFunction() {
var a = 0
DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
a = 1
print("First")
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
a = 3
print("Second")
DispatchQueue.main.async {
print(a)
}
}
}
}

就像等待异步一样

缺乏内置的语言级await async机制对Swift来说是一个严重的问题,可能会在未来的版本中得到弥补。与此同时,Swift基本上内置了no语言级的线程机制;您必须手动处理事情,通过与Cocoa/Objective-C(Grand Central Dispatch,NSOperation(或其他处理异步性的框架(Combine(进行对话。

您关注的特定问题是我所说的序列化异步性。有几种方法可以做到这一点。

Grand Central Dispatch

正如您所知,简单的方法是在第一个异步调用的执行处理程序结束时执行第二个异步调用。显然,这对于安排大量任务来说并不是很普遍。

您当然可以按照您概述的方式使用DispatchGroup。你对模式的描述几乎是正确的。您不需要最后一个wait,更重要的是,您必须在一开始就进入后台队列,因为在主队列上使用DispatchGroup是非法的;你的代码,就目前情况来看,在我们等待的时候,有效地阻塞了主队列,这是你不能做的事情。

操作队列

您可以创建一个串行OperationQueue并将Operation对象加载到其中。这是一个更通用的机制。可以使"操作"对象相互依赖。在Combine框架出现之前,这是最好的通用解决方案。

组合框架

如果您可以将自己限制在iOS13及更高版本中,那么Combine框架是以通用方式序列化异步性的最整洁的方法。请参阅,以获取进一步的讨论组合框架序列化异步操作。

最新更新