/// Convenience method to load data using an URL, creates and resumes an URLSessionDataTask internally. /// /// - Parameter url: The URL for which to load data. /// - Parameter delegate: Task-specific delegate. /// - Returns: Data and response. publicfuncdata(fromurl: URL, delegate: URLSessionTaskDelegate? =nil)asyncthrows -> (Data, URLResponse)
funcgetName()async -> String { /// 发起网络请求获取名字,这时候线程会卡住,直到请求完成了,就会把这个请求方法的返回值直接赋值给name,然后就会继续往下执行,把拿到的name给返回出去 let name =await requestName() return name }
/// 同理,当代码执行到这里的时候,调用异步方法,线程会卡住,然后方法里面去调用网络请求获取名字,然后等待网络请求返回之后,name就会被赋上值 let name =await getName()
/// Convenience method to upload data using an URLRequest, creates and resumes an URLSessionUploadTask internally. /// /// - Parameter request: The URLRequest for which to upload data. /// - Parameter fileURL: File to upload. /// - Parameter delegate: Task-specific delegate. /// - Returns: Data and response. publicfuncupload(forrequest: URLRequest, fromFilefileURL: URL, delegate: URLSessionTaskDelegate? =nil)asyncthrows -> (Data, URLResponse)
/// Convenience method to upload data using an URLRequest, creates and resumes an URLSessionUploadTask internally. /// /// - Parameter request: The URLRequest for which to upload data. /// - Parameter bodyData: Data to upload. /// - Parameter delegate: Task-specific delegate. /// - Returns: Data and response. publicfuncupload(forrequest: URLRequest, frombodyData: Data, delegate: URLSessionTaskDelegate? =nil)asyncthrows -> (Data, URLResponse)
/// Convenience method to download using an URLRequest, creates and resumes an URLSessionDownloadTask internally. /// /// - Parameter request: The URLRequest for which to download. /// - Parameter delegate: Task-specific delegate. /// - Returns: Downloaded file URL and response. The file will not be removed automatically. publicfuncdownload(forrequest: URLRequest, delegate: URLSessionTaskDelegate? =nil)asyncthrows -> (URL, URLResponse)
/// Convenience method to download using an URL, creates and resumes an URLSessionDownloadTask internally. /// /// - Parameter url: The URL for which to download. /// - Parameter delegate: Task-specific delegate. /// - Returns: Downloaded file URL and response. The file will not be removed automatically. publicfuncdownload(fromurl: URL, delegate: URLSessionTaskDelegate? =nil)asyncthrows -> (URL, URLResponse)
/// Convenience method to resume download, creates and resumes an URLSessionDownloadTask internally. /// /// - Parameter resumeData: Resume data from an incomplete download. /// - Parameter delegate: Task-specific delegate. /// - Returns: Downloaded file URL and response. The file will not be removed automatically. publicfuncdownload(resumeFromresumeData: Data, delegate: URLSessionTaskDelegate? =nil)asyncthrows -> (URL, URLResponse)
staticfunceatchquakes()asyncthrows { let endpointURL =URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv")!
// 跳过首行 因为是header描述不是地震数据 // 接着遍历提取强度、时间、经纬度信息 fortryawait event in endpointURL.lines.dropFirst() { let values = event.split(separator: ",") let time = values[0] let latitude = values[1] let longtitude = values[2] let magnitude = values[4] print("Magnitude \(magnitude) on \(time) at \(latitude)\(longtitude)") } }
/// Asynchronously advances to the next element and returns it, or ends the /// sequence if there is no next element. /// /// - Returns: The next element, if it exists, or `nil` to signal the end of /// the sequence. @inlinablepublicmutatingfuncnext()asyncthrows -> UInt8?
/// Creates the asynchronous iterator that produces elements of this /// asynchronous sequence. /// /// - Returns: An instance of the `AsyncIterator` type used to produce /// elements of the asynchronous sequence. publicfuncmakeAsyncIterator() -> URL.AsyncBytes } publicvar resourceBytes: URL.AsyncBytes { get } publicvar lines: AsyncLineSequence<URL.AsyncBytes> { get }
因为异步序列的遍历是一个耗时操作,所以我们也可以在需要的时候中断遍历(取消请求)
1 2 3 4 5 6 7 8 9 10
let task =Task(priority: .userInitiated) { do { tryawaitAsyncTest.eatchquakes() } catch { print(error) } }
letset = Set<String>(["1","2","3","4","5","6","7","8","9","10","11","12"]) let risky = RiskyCollector(deck:set) for i in 1...12 { DispatchQueue.global().async { _ = risky.send(card: "\(i)", to: risky) print(risky.deck) } }
Actor 通过引入 Actor 隔离解决了这个问题:除非异步执行,否则无法从 Actor 对象外部读取属性和方法,并且根本无法从 Actor 对象外部写入属性。 Swift 会自动将这些请求放入一个按顺序处理的队列中,以避免出现多线程竞争。
我们可以使用Actor重新实现一个SafeCollector,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
actor SafeCollector { var deck: Set<String> init(deck: Set<String>) { self.deck = deck }
letset=Set<String>(["1","2","3","4","5","6","7","8","9","10","11","12"]) let risky =SafeCollector(deck:set) for i in1...12 { Task { _=await risky.send(card: "\(i)", to: risky) print(await risky.deck) } }
在这个例子中有几件事情需要注意:
actor内对外暴露的方法都是异步方法,即使没有标记async,因为它会等到另一个 SafeCollector actor 能够处理请求。
actor 可以自由地、异步或以其他方式使用自己的属性和方法,但是当与不同的 actor 交互时,它必须始终异步完成。通过这些特性,Swift 可以确保永远不会同时访问所有与 actor 隔离的状态,更重要的是,这是在编译时完成的,以保证线程安全。
Actor 和 Class 有一些相似之处:
两者都是引用类型,因此它们可用于共享状态。
它们可以有方法、属性、初始值设定项和下标。
它们可以实现协议。任何静态属性和方法在这两种类型中的行为都相同。
除了 Actor 隔离之外,Actor 和 Class之间还有另外两个重要的区别:
Actor 目前不支持继承,这在未来可能会改变
所有 Actor 都隐式遵守一个新的 Actor Protocol
Global Actor
Global Actor 将 actor 隔离的概念扩展到了全局状态,即使状态和函数分散在许多不同的模块中,Global Actor 可以在并发程序中安全地使用全局变量,例如 Swift 提供的 @MainActor 限制属性和方法只能在主线程访问
funcnotOnTheMainActor()async { globalTextSize =12// error: globalTextSize is isolated to MainActor increaseTextSize() // error: increaseTextSize is isolated to MainActor, cannot call synchronously await increaseTextSize() // okay: asynchronous call hops over to the main thread and executes there }