Swift 中的 Actors 使用以及如何防止数据竞争_swift actor

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

func getValue() async -> Int {
    return value
}

}

// 创建 Counter Actor 实例
let counter = Counter()

// 在异步任务中调用 Actor 方法
Task {
await counter.increment()
let result = await counter.getValue()
print(“Counter value: (result)”)
}


在上面的代码中,Counter 是一个简单的 Actor,包含一个私有的 value 变量和两个方法 increment 和 getValue。increment 方法用于增加 value 的值,getValue 方法用于获取当前的 value 值。


在异步任务中,我们创建了一个 Counter 的实例 counter,并通过 await 关键字调用了 increment 和 getValue 方法。注意,在调用 Actor 的方法时,我们使用了 await 关键字来等待异步操作完成,并确保在访问和修改 Actor 数据时的安全性。


#### Actor 是引用类型,但与类相比仍然有所不同


Actor 是引用类型,简而言之,这意味着副本引用的是同一块数据。因此,修改副本也会修改原始实例,因为它们指向同一个共享实例。你可以在我的文章[Swift 中的 Struct 与 class 的区别](https://bbs.csdn.net/topics/618679757)中了解更多这方面的信息。


**然而,与类相比,Actor 有一个重要的区别:他们不支持继承。**


![Swift中的Actor几乎和类一样,但不支持继承。](https://img-blog.csdnimg.cn/img_convert/88310db20f1013d4b161632eba525acc.png)


Swift 中的 Actor 几乎和类一样,但不支持继承。


不支持继承意味着不需要像便利初始化器和必要初始化器、重写、类成员或 `open` 和 `final` 语句等功能。


**然而,最大的区别是由 Actor 的主要职责决定的,即隔离对数据的访问。**


### 为什么会出现数据竞争


数据竞争是多线程/并发编程中常见的问题,它发生在多个线程同时访问共享数据,并且至少其中一个线程对该数据进行了写操作。当多个线程同时读写共享数据时,数据的最终结果可能会产生不确定性,导致程序出现错误的行为。


数据竞争发生的原因主要有以下几个方面:


1、竞态条件(Race Condition):当多个线程在没有适当同步的情况下并发地访问共享数据时,它们的执行顺序是不确定的。这可能导致数据的交错读写,从而导致数据的最终结果出现错误。


2、缺乏同步:如果多个线程在没有适当的同步机制的情况下访问共享数据,就会产生数据竞争。例如,多个线程同时对同一个变量进行写操作,而没有使用互斥锁或其他同步机制来保证原子性。


3、共享资源的修改:当多个线程同时对共享资源进行写操作时,就会产生数据竞争。如果没有适当的同步机制来保护共享资源的一致性,就会导致数据竞争。


4、可见性问题:多个线程可能具有各自的本地缓存或寄存器,这导致它们对共享数据的可见性存在延迟。当一个线程修改了共享数据,其他线程可能无法立即看到这个修改,从而导致数据竞争。


数据竞争可能导致程序出现各种问题,包括不确定的结果、崩溃、死锁等。为了避免数据竞争,需要采取适当的并发控制措施,例如使用锁、互斥量、信号量等同步机制来保护共享数据的访问,或者使用并发安全的数据结构来代替共享数据。


在 Swift 中,引入了一些并发编程的机制,如 async/await 和 Actor,可以帮助开发者更容易地处理并发问题和避免数据竞争。但仍需要开发者在编写并发代码时注意使用正确的同步机制和遵循最佳实践,以确保数据的安全和正确性。


### 如何防止数据竞争


Actors 通过限制同一时间只有一个任务可以访问 Actor 中的数据来防止数据竞争。这种限制确保了数据的一致性和线程安全性,而无需显式使用锁或其他同步机制。


Actors 还提供了数据的原子性访问和修改操作。在 Actor 内部,数据可以在异步环境中自由地修改,而不需要额外的同步操作。同时,Actors 会保证 Actor 的内部状态在处理完一个消息之前不会被其他任务访问,从而避免了并发问题。


在上面的示例中,我们可以看到在异步任务中通过 await 关键字调用 Counter 的方法。这样做可以确保在不同的任务中对 Counter 的访问是串行的,从而避免了数据竞争和其他并发问题。


Actors 是 Swift 中用于并发编程的一种模型,它通过异步消息传递来保护共享数据,并防止数据竞争。下面是一些阐述 Actors 的使用和防止数据竞争的关键要点:


1、定义 Actor:使用 actor 关键字来定义一个 Actor 类,将要保护的数据和相关操作封装在其中。例如:



actor MyActor {
private var sharedData: Int = 0

func performTask() {
    // 对共享数据进行操作
}

}


2、异步访问:通过 async 和 await 关键字来标记异步函数和等待异步结果。只有通过异步函数或方法访问 Actor 中的数据才是安全的。例如:



actor MyActor {
private var sharedData: Int = 0

func performTask() async {
    sharedData += 1
    await someAsyncOperation()
    let result = await anotherAsyncOperation()
    // 对共享数据进行操作
}

}


3、 发送异步消息:通过在 Actor 实例上使用 await 关键字来发送异步消息,并调用 Actor 中的方法。这样做可以确保对 Actor 的访问是串行的,从而避免了数据竞争。例如:



let myActor = MyActor()

Task {
await myActor.performTask()
}


4、数据保护:由于 Actors 限制了同一时间只能执行一个任务,因此可以保证对共享数据的访问是串行的,从而避免了数据竞争。Actors 还提供了内部状态的保护,确保在处理一个消息之前不会被其他任务访问。


### 使用 async/await 访问数据


在 Swift 中,使用 async/await 关键字来进行异步访问数据是一种安全且方便的方式,特别适用于访问 Actor 中的共享数据。下面是一些示例代码来说明如何使用 async/await 访问数据:



actor MyActor {
private var sharedData: Int = 0

func readData() async -> Int {
    return sharedData
}

func writeData(value: Int) async {
    sharedData = value
}

}

// 创建 MyActor 实例
let myActor = MyActor()

// 异步读取共享数据
Task {
let data = await myActor.readData()
print(“Shared data: (data)”)
}

// 异步写入共享数据
Task {
await myActor.writeData(value: 10)
print(“Data written successfully”)
}


在上面的示例中,readData 方法和 writeData 方法被标记为 async,表示它们是异步的。通过 await 关键字,我们可以在异步任务中等待数据的读取和写入操作完成。


使用 await 关键字调用 readData 方法时,任务会等待直到共享数据的读取操作完成,并将结果返回。类似地,使用 await 关键字调用 writeData 方法时,任务会等待直到共享数据的写入操作完成。


需要注意的是,在使用 async/await 访问数据时,要确保访问的方法或属性是异步的。对于 Actor 中的方法,可以在其声明前加上 async 关键字,表示它们是异步的。对于属性,可以将其声明为异步的计算属性。


### 防止不必要的暂停


在上面的例子中,我们正在访问我们 Actor 的两个不同部分。首先,我们更新吃食的鸡的数量,然后我们执行另一个异步任务,打印出吃食的鸡的数量。每个 `await` 都会导致你的代码暂停,以等待访问。在这种情况下,有两个暂停是有意义的,因为两部分其实没有什么共同点。然而,你需要考虑到可能有另一个线程在等待调用 `chickenStartsEating`,这可能会导致在我们打印出结果的时候有两只吃食的鸡。


为了更好地理解这个概念,让我们来看看这样的情况:你想把操作合并到一个方法中,以防止额外的暂停。例如,设想在我们的 `actor` 中有一个通知方法,通知观察者有一只新的鸡开始吃东西:



extension ChickenFeeder {
func notifyObservers() {
NotificationCenter.default.post(name: NSNotification.Name(“chicken.started.eating”), object: numberOfEatingChickens)
}
}


我们可以通过使用 `await` 两次来使用此代码:



let feeder = ChickenFeeder()
await feeder.chickenStartsEating()
await feeder.notifyObservers()


然而,这可能会导致两个暂停点,每个 `await` 都有一个。相反,我们可以通过从 `chickenStartsEating` 中调用 `notifyObservers` 方法来优化这段代码:



func chickenStartsEating() {
numberOfEatingChickens += 1
notifyObservers()
}


由于我们已经在 Actor 内有了同步的访问,我们不需要另一个等待。这些都是需要考虑的重要改进,因为它们可能会对性能产生影响。


### 非隔离(nonisolated)访问


在 Swift 中,非隔离(nonisolated)访问是指在一个异步函数内部访问类、结构体或枚举的非隔离成员。异步函数默认情况下是隔离的,这意味着在异步函数内部只能访问该类型的隔离成员。但有时候我们需要在异步函数中访问非隔离成员,这时就可以使用非隔离访问。


为了进行非隔离访问,需要使用 nonisolated 关键字来修饰访问权限。例如,假设有一个类 MyClass,其中有一个非隔离成员属性 value:



class MyClass {
nonisolated var value: Int = 0
}


现在,我们可以在异步函数内部访问 MyClass 的 value 属性:



func asyncFunction() async {
let instance = MyClass()
await doSomething(with: instance.value) // 非隔离访问
}



**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/17dc1918aed80842089f27b9aeb352fe.png)
![img](https://img-blog.csdnimg.cn/img_convert/5ef74380b1ffd26876dd9361e7d0ab93.png)

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

715793883082)]
[外链图片转存中...(img-HORB97F9-1715793883082)]

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码的功能是对电影信息统计表格的演员组合进行统计,并将结果输出到一个新的 Excel 表格。 1. 首先,通过 openpyxl 库的 `load_workbook` 方法读取 Excel 表格文件,得到一个 Workbook 对象,并将第一个工作表赋值给 `ws1` 变量,同时创建一个名为 `演员组合参演统计` 的新工作表,并将其赋值给 `ws2` 变量。 2. 定义一个字典 `actor_comb_dict`,用于存储演员组合及其参演电影数量和电影列表。 3. 对 `ws1` 工作表的每一行进行遍历,通过 `iter_rows` 方法获取每一行的数据,并将演员名称按逗号分隔成列表 `actors`。 4. 对于每个演员列表的两个演员,将其组合成一个元组 `actor_comb`,并将其按照字典的方式存入 `actor_comb_dict` 。如果该组演员组合在字典不存在,则将其添加到字典,并将其参演电影数量设为 1,参演电影列表设为当前电影名称;否则将其参演电影数量加 1,参演电影列表加上当前电影名称。 5. 对于演员列表包含三个或三个以上演员的电影,对其的三个演员进行组合,将组合结果存入列表 `actor_combList` 。如果 `actor_combList` 的元素个数大于等于 2,则从随机选取两个元素,将其按照字典的方式存入 `actor_comb_dict` 。否则,将 `actor_combList` 的每个元素按照字典的方式存入 `actor_comb_dict` 。 6. 在新工作表 `ws2` 的第一行分别添加列名为 `演员组合`、`演员组合参演电影数量` 和 `演员组合参演电影列表`。 7. 遍历 `actor_comb_dict` 字典的所有键值对,在新工作表 `ws2` 新增一行,分别将演员组合、演员组合参演电影数量和演员组合参演电影列表输出到对应的单元格。最后将行号 `row_num` 加 1,以便输出下一行数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值