swift 迁移到Swift6并发导致问题处理

发布于:2025-02-10 ⋅ 阅读:(52) ⋅ 点赞:(0)

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()
        }
    }

网站公告

今日签到

点亮在社区的每一天
去签到