preconcurrency 使用
/*
在 Swift 5.5 引入并发模型后,编译器会对潜在的数据竞争和不安全的并发代码发出警告或错误。然而,某些旧代码或第三方库可能尚未完全适配这些新规则。@preconcurrency 提供了一种临时解决方案,允许你在不修改代码的情况下继续使用这些 API。
使用场景
@preconcurrency 适用于以下场景:
旧代码迁移:在将旧代码迁移到 Swift 并发模型时,暂时抑制并发相关的警告。
第三方库支持:在使用尚未适配 Swift 并发模型的第三方库时,避免编译器警告。
逐步迁移:在逐步迁移到 Swift 并发模型的过程中,临时禁用某些部分的并发检查。
注意事项
临时解决方案:
@preconcurrency 是一个临时解决方案,建议尽快迁移到 Swift 并发模型,而不是长期依赖它。
潜在风险:
使用 @preconcurrency 可能会隐藏潜在的数据竞争和并发问题,因此需要谨慎使用。
逐步迁移:
在逐步迁移到 Swift 并发模型的过程中,可以使用 @preconcurrency 来临时禁用某些部分的并发检查。
总结
@preconcurrency 用于在并发上下文中标记某些 API 或类型,以抑制与并发相关的警告或错误。
它适用于旧代码迁移、第三方库支持以及逐步迁移到 Swift 并发模型的场景。
使用时应谨慎,避免隐藏潜在的数据竞争和并发问题。
*/
/*
你可以将整个类型标记为 @preconcurrency,以抑制与该类型相关的并发警告。
*/
class m11 {
}
class Test1111111{
/*
你可以将特定函数标记为 @preconcurrency,以抑制与该函数相关的并发警告。
*/
func testtt(){}
}
//标记导入的模块为 @preconcurrency
import UIKit
nonisolated使用
在 Swift 并发编程中,nonisolated 是一个关键字,用于标记某个方法或属性不受 Actor 的隔离保护。这意味着被标记为 nonisolated 的成员可以在 Actor 外部直接访问,而无需通过 await 等待
nonisolated 通常用于以下场景:
访问不可变状态:如果某个属性是只读的且不会改变,可以将其标记为 nonisolated。
计算属性:如果某个计算属性不依赖于 Actor 的状态,可以将其标记为 nonisolated。
静态方法或属性:静态成员通常不依赖于 Actor 的实例状态,可以标记为 nonisolated。
actor MyActor{
private var counter:Int = 0
public func increment(){
counter += 1
}
/*
如果一个方法不访问 Actor 的状态,可以将其标记为 nonisolated
*/
nonisolated func getDescripter()->String{
return "This is MyActor"
}
/*
如果一个属性是只读的且不依赖于 Actor 的状态,可以将其标记为 nonisolated。
*/
nonisolated var descripter:String{
return "This is MyActor"
}
/*
如果计算属性不依赖于 Actor 的状态,可以将其标记为 nonisolated。
*/
nonisolated var isPositive:Bool{
return descripter.count > 0
}
private let id:UUID = UUID()
/*
如果 Actor 的某个属性是只读的且不会改变,可以将其标记为 nonisolated。
*/
nonisolated var uniquId:String{
return id.uuidString
}
/*
静态成员(属性和方法)通常不依赖于 Actor 的实例状态,可以标记为 nonisolated。
*/
nonisolated static let defaultname:String = "MyActor"
nonisolated static func getDefaultName()->String{
return defaultname
}
/*
不依赖于 Actor 状态的方法,可以标记为 nonisolated。
*/
nonisolated func print(){
Swift.print("Hello wrold")
}
}
func test1(){
let actor = MyActor()
print("\(actor.getDescripter())")
print("\(actor.descripter)")
Task {
await actor.increment()
}
}
MainActor使用
更新 UI:所有与 UI 相关的操作必须在主线程上执行。
访问主线程绑定的资源:例如 Core Data 的 NSManagedObjectContext(如果绑定到主线程)。
确保线程安全:避免在多线程环境中访问共享状态。
注意事项
性能:
过度使用 @MainActor 可能会导致主线程拥塞,影响 UI 响应性。确保只有必要的代码在主线程上运行。
死锁:
如果在 @MainActor 上下文中同步调用其他 @MainActor 方法,可能会导致死锁。避免在主线程上执行阻塞操作。
兼容性:
@MainActor 是 Swift 5.5 引入的特性,确保你的项目支持 Swift 并发编程。
/*
你可以将整个类型标记为 @MainActor,这样该类型的所有方法和属性都会在主线程上运行
*/
class DemoClass1{
}
/*
如果你只想让某个方法在主线程上运行,可以单独标记该方法。
*/
func updateUI(){}
/*
你也可以将属性标记为 @MainActor,以确保对该属性的访问和修改都在主线程上进行
*/
public var titleText:String?
/*
如果你需要在非 @MainActor 上下文中执行一段代码,可以使用 MainActor.run 来将代码切换到主线程
*/
func fetchData()async{
try? await Task.sleep(nanoseconds: 1000000000)
await MainActor.run {
print("run code in main thread")
}
}
/*
MainActor.assumeIsolated 是 Swift 并发编程中的一个工具,用于在编译时强制检查当前代码是否在 MainActor 上下文中执行。它可以帮助你在编写代码时明确要求某些操作必须在主线程上运行,从而避免潜在的多线程问题。
MainActor.assumeIsolated 适用于以下场景:
明确要求主线程执行:当你希望确保某段代码必须在主线程上运行时,可以使用 MainActor.assumeIsolated 来强制检查。
调试和验证:在调试时,你可以使用它来验证代码是否在主线程上运行。
避免潜在的多线程问题:通过强制检查,可以避免在非主线程上意外执行 UI 更新或其他主线程绑定的操作。
*/
func testd1(){
//主要用于调试,不会切换线程,如果不是主线程区块代码不会执行
MainActor.assumeIsolated {
print("run code in main thread")
}
// MainActor.shared;
}
关于Actor导致的数据竞争问题解决方法
/*
自定义全局Actor
*/
struct MMActor{
static let shared: bobActor1 = bobActor1()
typealias ActorType = bobActor1
actor bobActor1{
}
}
class testMMDemo1{
func updateUI(){
}
func doBackGroundWord(){
print("后台任务执行")
//切换到ui线程更新
//会导致数据竞争问题,下面详解
//updateUI()
}
}
/*
数据竞争 是并发编程中的常见问题,可能导致未定义行为。
Sendable 协议用于标记可以安全跨线程传递的类型。
解决方法包括:
使用值类型(struct 或 enum)。
将类标记为 @MainActor。
使类遵循 Sendable 协议。
捕获特定属性而不是 self。
使用 actor 封装状态。
避免隐式捕获 self,使用 weak 或 unowned。
*/
/*
使用值类型(struct 或 enum)。
*/
struct testMMDemo2{
func updateUI(){
}
func doBackGroundWord() async{
print("后台任务执行")
//切换到ui线程更新,异步代码
await updateUI()
}
}
/*
将类标记为 @MainActor。
如果你的类需要在主线程上运行,可以将整个类标记为 @MainActor。这样,所有对 self 的访问都会自动在主线程上执行,避免数据竞争
*/
class testMMDemo3{
func updateUI(){
}
func doBackGroundWord() async{
print("后台任务执行")
//切换到ui线程更新
await MainActor.run {
updateUI()
}
}
}
/*
使用 Sendable 包装
如果必须使用类,并且需要跨线程传递,可以使类遵循 Sendable 协议。注意,Sendable 要求类的属性是不可变的,或者属性本身是线程安全的
*/
final class testMMDemo4:Sendable{
func updateUI(){
}
func doBackGroundWord() async{
print("后台任务执行")
//切换到ui线程更新
await MainActor.run {
updateUI()
}
}
}
/*
捕获特定属性而不是 self
如果只需要访问类的某个属性,可以捕获该属性而不是 self。这样可以避免传递整个 self。
*/
class testMMDemo5{
var valeue:Int = 0
// @MainActor
// func updateUI(){
//
// }
func doBackGroundWord() async{
print("后台任务执行")
//切换到ui线程更新
await MainActor.run {[valeue] in
print("\(valeue)")
}
}
}
actor testMMDemo6{
func updateUI(){
}
func doBackGroundWord() async{
print("后台任务执行")
//切换到ui线程更新
await MainActor.run {
updateUI()
}
}
}
/*
避免隐式捕获 self,使用 weak 或 unowned。
在闭包中,如果使用了 self 的属性或方法,Swift 会隐式捕获 self。为了避免数据竞争,可以显式捕获 self 并标记为 weak 或 unowned(如果适用)
*/
class testMMDemo8{
func updateUI(){
}
func doBackGroundWord() async{
print("后台任务执行")
Task{[weak self]in
guard let self = self else{
return
}
//切换到ui线程更新
await MainActor.run {
self.updateUI()
}
}
}
}
Task使用详情
func test1() async{
//创建任务,任务会立即开始执行
let task1 = Task{
print("Task is running")
try await Task.sleep(nanoseconds: 100000000)
return "Task complete"
}
/*
Task.detached 创建一个与当前任务上下文无关的独立任务。它不会继承当前任务的优先级或上下文。
*/
let task2 = Task.detached {
print("Task is running")
try await Task.sleep(nanoseconds: 100000000)
return "Task complete"
}
/*
使用 await 获取结果
你可以通过 await 获取任务的结果。
*/
let result = await task2.result
print("\(result)")
/*
cancel() 方法取消任务。任务会在下一个检查点(如 await)时停止执行。
*/
let task3 = Task{
for i in 0...10{
try await Task.sleep(nanoseconds: 500000000)
print("")
if Task.isCancelled{
print("Task was cancelled")
return
}
}
}
Task{
try await Task.sleep(nanoseconds: 1500000)
task3.cancel()
}
// 可以通过 Task(priority:) 设置任务的优先级。
let task4 = Task(priority: .high) {
}
/*
任务组允许你并发执行多个任务,并等待它们全部完成
*/
func fetchDatas()async throws ->[Data?]{
func netrequest(url:URL)async throws->Data{
let (data,_) = try await URLSession.shared.data(from: url)
return data
}
let ret = await withTaskGroup(of: Optional<Data>.self) { group in
var datas = [Data?]()
let urls = [
URL(string: "https://ww.baidu.com"),
URL(string: "https://ww.baidu.com"),
URL(string: "https://ww.baidu.com"),
URL(string: "https://ww.baidu.com"),
]
urls.forEach { url in
group.addTask {
try? await netrequest(url: url!)
}
}
for await result in group{
datas.append(result)
}
return datas
}
return ret
}
//Task 错误处理
func testVal3() async{
let task = Task<String,Error>{
throw NSError(domain: "Error", code: 10)
}
//通过异常处理
do{
let value:String = try await task.value
}catch let exception{
print("error:\(exception)")
}
//处理Result方式
let result:Result<String,Error> = await task.result
switch result {
case .success(let success):
let suc:String = success
print("\(success)")
case .failure(let failure):
let error:Error = failure
print("error:\(failure)")
}
}
/*
使用 Task.bind(to:) 绑定任务
*/
/*
可以通过 Task.isCancelled 检查任务是否被取消,或者通过 Task.currentPriority 获取当前任务的优先级
*/
func test1111() -> String{
Task{ in
print("Task is running on the main actor")
try await Task.sleep(nanoseconds: 1_000_000_000) // 模拟异步操作
if Task.isCancelled{
print("Task is canceled")
}
let priority = Task.currentPriority
}
return "Task completed on main actor"
}
}
}
/*
Task.yield() 是 Swift 并发编程中的一个工具,用于在当前任务中主动让出执行权,以便其他任务有机会运行。它通常用于长时间运行的任务中,以避免阻塞线程或独占资源。
Task.yield() 适用于以下场景:
长时间运行的任务:在任务中执行大量计算或循环时,使用 Task.yield() 可以让其他任务有机会运行,避免阻塞线程。
公平调度:在并发环境中,确保所有任务都能公平地获得执行机会。
避免死锁:在复杂的并发场景中,使用 Task.yield() 可以避免任务之间的相互阻塞。
注意事项
性能影响:
频繁调用 Task.yield() 可能会增加任务切换的开销,影响性能。仅在必要时使用。
任务调度:
Task.yield() 只是建议让出执行权,实际调度行为取决于系统的任务调度器。
适用场景:
适用于长时间运行的任务或需要公平调度的场景。对于短任务,通常不需要使用 Task.yield()。
*/
func testTaskyield(){
func performWork() async {
for i in 1...10 {
print("Working on task \(i)")
await Task.yield() // 让出执行权
}
}
Task {
await performWork()
}
}