值类型的实例不共享:每个线程都有自己的副本。*这意味着每个线程都可以读写其实例,而不必担心其他线程在做什么。
然后我被带到了这个答案及其评论
并被告知:
从多个线程访问的数组本身本身不是线程安全的,因此所有交互必须同步。
&告诉我 每个线程都有自己的副本
如果一个线程正在更新数组(大概是这样,您就可以从另一个队列中看到该编辑内容),那根本就不适用
根本不适用 <-为什么不呢?
我最初以为所有这些事情都是在发生,因为数组(即值类型)被包装到一个类中,但是令我惊讶的是,我被告知不是真的!所以我又回到了Swift 101:D
根本问题是“每个线程都有自己的副本”的解释。
是的,我们经常使用值类型,通过为每个线程提供自己的对象副本(例如数组)来确保线程安全。但这与声称值类型保证每个线程将获得自己的副本不同。
具体来说,使用闭包,多个线程可以尝试变异同一值类型的对象。这是一个代码html" target="_blank">示例,显示了一些与Swift Array
值类型交互的非线程安全代码:
let queue = DispatchQueue.global()
var employees = ["Bill", "Bob", "Joe"]
queue.async {
let count = employees.count
for index in 0 ..< count {
print("\(employees[index])")
Thread.sleep(forTimeInterval: 1)
}
}
queue.async {
Thread.sleep(forTimeInterval: 0.5)
employees.remove(at: 0)
}
(您通常不会添加sleep
调用;我只是将它们添加到清单竞争条件中,否则这些条件很难重现。您也不应在没有同步的情况下从多个线程中更改对象,但是我这样做是为了说明问题。)
在这些async
调用中,您仍在引用employees
先前定义的同一数组。因此,在此特定示例中,我们将看到它输出“ Bill”,它将跳过“
Bob”(即使已删除“ Bill”),它将输出“ Joe”(现在是第二项),并且那么尝试访问数组中的第三个项目将崩溃,该数组现在只剩下两个项目。
现在,我在上面说明的所有事情是,一个值类型 可以
在一个线程中被另一个线程使用,而又被另一个线程使用,从而违反了线程安全性。实际上,在编写不是线程安全的代码时,可能会出现一系列更基本的问题,但是以上只是一个稍微作弊的示例。
但是,您可以employees
通过在第一个async
调用中添加一个“捕获列表”
来表示要使用原始employees
数组的副本,从而确保该单独的线程获得其数组的副本:
queue.async { [employees] in
...
}
或者,如果将此值类型作为参数传递给另一个方法,则将自动获得此行为:
doSomethingAsynchronous(with: employees) { result in
...
}
在这两种情况中的任何一种情况下,您都将享受值语义,并看到原始数组的副本(或写时复制),尽管原始数组可能已在其他地方进行了突变。
最重要的是,我的观点仅仅是值类型不能 保证
每个线程都有自己的副本。该Array
类型不是线程安全的(也不是许多其他可变值类型)。但是,像所有值类型一样,Swift提供了简单的机制(其中一些是完全自动和透明的),这些机制将为每个线程提供自己的副本,从而使编写线程安全代码变得更加容易。
这是另一个示例,其中另一个值类型使问题更加明显。这是一个示例,其中编写线程安全代码失败返回语义上无效的对象:
let queue = DispatchQueue.global()
struct Person {
var firstName: String
var lastName: String
}
var person = Person(firstName: "Rob", lastName: "Ryan")
queue.async {
Thread.sleep(forTimeInterval: 0.5)
print("1: \(person)")
}
queue.async {
person.firstName = "Rachel"
Thread.sleep(forTimeInterval: 1)
person.lastName = "Moore"
print("2: \(person)")
}
在此示例中,第一个打印语句将有效地说“ Rachel Ryan”,它既不是“ Rob Ryan”也不是“ Rachel
Moore”。简而言之,我们正在检查Person
内部状态不一致的情况。
但是,同样,我们可以使用捕获列表来享受价值语义:
queue.async { [person] in
Thread.sleep(forTimeInterval: 0.5)
print("1: \(person)")
}
在这种情况下,它会说“ Rob
Ryan”,而忽略了原件Person
可能正在被另一个线程变异的事实。(很明显,真正的问题并不仅是通过在第一次async
调用中使用值语义来解决的,而且还同步第二次async
调用和/或在那里使用值语义来解决。)
如果我得到一个JWT并且我可以解码有效负载,那怎么会安全呢?难道我不能直接从报头中抓取令牌,解码并更改有效负载中的用户信息,然后用同样正确的编码秘密将其发回吗? 我知道他们必须是安全的,但我只是真的想了解技术。我错过了什么?
问题内容: 为了掌握Redis的一些基础知识,我遇到了一篇有趣的博客文章。 作者指出: Redis是具有epoll / kqueue的单线程,并且在I / O并发方面可以无限扩展。 我肯定会误解整个线程问题,因为我发现此语句令人困惑。如果程序是单线程的,它如何并发执行任何操作?如果服务器仍然是单线程的,为什么Redis操作是原子的那么好呢? 有人可以阐明这个问题吗? 问题答案: 好吧,这取决于
我试图编写java代码,从用户输入中获取十个整数。然后我必须找到重复的数字。 “为什么我仍然会出错?
问题内容: 我看过OpenJDK的OpenJDK源代码,似乎所有写操作都受同一锁保护,而读操作则根本不受保护。据我了解,在JMM下,对变量的所有访问(读和写)都应受锁保护,否则可能会发生重新排序的效果。 例如,method包含以下几行(处于锁定状态): 另一方面,该方法仅起作用。 在我对JMM的理解中,这意味着如果将语句1-4重新排序为1-2(new)-4-2(copyOf)-3 ,则可能会在不一
所以我有一个问题,当我计算一个数字时,比如说15,我必须显示这个:15=3x5,但我得到的是3x5x5,我不知道如何使它变成这样,所以它只显示3x5。还有一个问题是,我输入的数字是否是素数。有办法解决这个问题吗?我只需要这些,然后再编辑其他东西。
做对了"。事实证明,可以用来表示任何类似ListT-dod-right的东西。引用加布里埃尔·冈萨雷斯的话: 请注意,您可以仅使用变压器依赖项构建任何(不仅仅是管道中的那个)。例如,下面是如何实现: 这将在那里键入check作为any,并为所有人做正确的事情。 所以我的问题是:对于管道的消费者部分,是否有一个对偶到和到? 要求: 从不使用,只返回(或从不返回),但使用的管道可以表示为“双重到列表”