这将是一个非常基本的问题。关于结构如何是线程安全的,类是不安全的,我有一些困惑。
以下是我认为线程安全和不安全的含义。
线程不安全--如果允许多个线程同时修改任何对象。
线程安全-如果不允许任何对象同时修改多个线程。
下面是我尝试过的结构代码:
var basket = Basket()
func threadSafetyForStruct1() {
let queue = DispatchQueue(label: "threadSafety1", attributes: [.concurrent])
queue.async {
for age in 1...30 {
basket.amount = age
}
}
}
func threadSafetyForStruct2() {
let queue = DispatchQueue(label: "threadSafety2", attributes: [.concurrent])
queue.async {
for age in 31...60 {
basket.amount = age
}
}
}
threadSafetyForStruct1()
threadSafetyForStruct2()下面是我尝试过的类代码:
let animal = Animal()
func threadSafetyForClass1() {
let queue = DispatchQueue(label: "threadSafety1", attributes: [.concurrent])
queue.async {
for age in 1...30 {
animal.age = age
animal.name = "name\(age)"
}
}
}
func threadSafetyForClass2() {
let queue = DispatchQueue(label: "threadSafety2", attributes: [.concurrent])
queue.async {
for age in 31...60 {
animal.age = age
animal.name = "name\(age)"
}
}
}
threadSafetyForClass1()
threadSafetyForClass2()这两种代码都成功运行。没有运行时错误,也没有编译时间。两者在末尾都有意想不到的值,这在我运行并发队列时是预期的。
我在这里错过了什么?
发布于 2022-01-21 09:10:17
线程安全是一个广泛的话题。但是,在程序执行结束时得到意外值的事实表明,代码并不是线程安全的。
这样,我相信您在使用struct和class时希望得到某种不同的结果。也许是撞车之类的?既然如此,它为什么不坠毁呢?
,
Int和String属性,这些属性是值类型。我做了一些测试,发现Swift中价值类型的分配大多是线程安全的。这样你的程序就不会崩溃。--如果您的对象如下所示:
class Name{
var first: String = ""
var last: String = ""
init(){}
}
struct Animal{
var age: Int
var name: Name
init(){
age = 0
name = Name()
}
}您试着从两个线程中同时读取和分配Name的新实例--现在这有可能使程序崩溃。
在分配新值时,会得到这样的错误:
malloc: Double free of object如果您使用的是名称的struct实现,则不会发生同样的情况。那么,这是否使struct线程比class更安全呢?更多的线程安全-是的。但是你的程序的结果仍然是不可预测的,所以它不能解决这个问题。
在您的代码中,您正在每个线程中执行30次读取和分配任务。在这30种操作之间没有任意复杂的代码位可以执行。
如果您要在每次操作之前引入一个人为的随机延迟(类似于usleep) (并且要专门使用class实例),那么您可能会遇到程序崩溃。
备注
我做了一个小的Git的版本,你的程序,确实设法崩溃。
发布于 2022-01-21 08:21:54
首先,结构是值类型,这意味着它们通常是通过值传递的(副本是创建的)。但也不总是这样。当您在闭包中使用它们( queue.async调用中的函数)时,它会被引用捕获。因此,在这种情况下,结构和类之间没有真正的区别。
现在,仅仅因为某些东西是一个结构,并不能使它自动线程安全。线程安全是一个广泛的话题。一个通用的定义是:一个函数(或函数的集合)是线程安全的,如果当您并行执行它们时,结果就好像它们是按某种任意顺序顺序执行的一样。因此,线程的使用只是为了优化目的,它不以任何方式改变行为。
当您按值传递事物时,就会创建副本。所以不管你在副本上做什么,它都不会影响外部。从这个意义上说,事情变得线程安全,因为资源不再是共享的。但是,当通过引用传递时,您将共享资源,因此出现了线程安全问题。
总结:线程安全并不适用于数据本身(struct或class),而是应用于您访问共享数据的方式。
在末尾都有意想不到的值,这是意外的,因为我正在运行并发队列。
这完全和预期的一样。您确实有一个队列,但是由于dispatcher是并发的,所以会有多个线程使用该队列。这些线程并行运行您的内部函数。没有锁(或其他同步原语),您的代码就不是线程安全的,因为多个线程将同时访问数据。在最好的情况下,结果是交错的。在最坏的情况下,它可能会完全崩溃(除非Swift保证分配是线程安全的,这一点我不确定)。
发布于 2022-01-21 09:01:59
在执行for-循环时,由于上下文切换,您正在观察不一致的结果。一个循环运行一段时间,然后另一个循环运行一段时间,它们来回跳到循环结束为止。
不能保证每个循环都会在不中断其他队列的情况下开始运行到完成。
仅仅因为某物是一个结构并不意味着它是线程安全的。您必须在从不同队列访问结构的区域中保护结构。在这种情况下,您需要保护for-循环中的结构,确保您的循环在其他队列有机会跳入之前启动并完成执行。
其中一种方法是使用NSLock,但是有许多方法可以同步访问代码块。
var basket = Basket()
let basketLock = NSLock()
func threadSafetyForStruct1() {
let queue = DispatchQueue(label: "threadSafety1", attributes: [.concurrent])
queue.async {
basketLock.lock()
for age in 1...30 {
basket.amount = age
}
basketLock.unlock()
}
}
func threadSafetyForStruct2() {
let queue = DispatchQueue(label: "threadSafety2", attributes: [.concurrent])
queue.async {
basketLock.lock()
for age in 31...60 {
basket.amount = age
}
basketLock.unlock()
}
}
threadSafetyForStruct1()
threadSafetyForStruct2()当第一个队列获得锁时,另一个队列将被阻塞,直到第一个队列释放锁为止。只有这样,第二个队列才能获得锁并执行其操作。
https://stackoverflow.com/questions/70797142
复制相似问题