苹果对多线程的引用和值类型的描述 [英] Apple's description of reference and value types with multiple threads

查看:150
本文介绍了苹果对多线程的引用和值类型的描述的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读苹果的文档。我以为我知道什么时候选择一个值类型,什么时候选择一个引用类型,但是我回到了Swif101。文件说:



  • 值类型:数据将用于代码跨多个线程。

  • 参考类型:您想要创建共享的,可变的状态


不是多个线程共享的引用类型?这两行有什么区别?

解决方案

正如其他人所指出的,引用类型总是传递一个指向该对象的指针,这是您想要共享 ,可变状态(如您所引用的文档所述)。显然,如果您在多个线程中变异/访问引用类型,请确保同步其访问(通过专用串行队列,读写器模式,锁等)。



虽然价值类型有点复杂,是的,正如其他人所指出的那样,如果您将一个值类型作为参数传递给另一个线程上的某个方法,那么您本质上正在使用该值类型的副本(Josh关于copy-on-写,尽管)。这确保传递给该方法的该对象的完整性。这很好(已经被这里的其他答案充分覆盖了)。



但是当你处理闭包时,它会变得更加复杂。例如,请考虑以下内容:

  struct Person {
var firstName:String
var lastName: String
}

var person = Person(firstName:Rob,lastName:Ryan)

DispatchQueue.global()。async {
Thread.sleep(forTimeInterval:1)
print(1:\(person))
}

person.firstName =Rachel
主题.sleep(forTimeInterval:2)
person.lastName =Moore
print(2:\(person))

显然,你通常不会有$ code> sleep ,但是我正在这样做来说明这点:即使我们正在处理一个值类型和多个线程,个人你在闭包中的引用是你正在处理的相同的实例主线程(或正在运行的任何线程),而不是它的副本。如果你正在处理一个可变对象,那不是线程安全的。



我已经设计了这个例子来说明这一点,在哪里打印语句在上面的闭包中将报告Rachel Ryan,有效地显示不一致状态的 Person 值类型的状态。



使用值类型的闭包,如果要享受价值语义,您必须更改 async 调用以单独使用变量:

  let separatePerson = person 
queue.async {
Thread.sleep(forTimeInterval:1)
print(1:\(separatePerson))
}

或者更容易,使用捕获列表,它表示封闭应该捕获什么值类型变量:

  .async {[person] in 
Thread.sleep(forTimeInterval:1)
print(1:\(person))
}

任何一个例如,您现在享受价值语义,复制对象,而 print 语句将正确地报告Rob Ryan,即使原来的对象在另一个线程上被突变。



所以,如果你正在处理值类型和闭包,值类型可以跨线程共享,除非你明确地使用捕获列表(或一些等价物)来享受价值语义(即根据需要复制对象)


I am reading from Apple's documentation. I thought I knew when to choose a value type and when to choose a reference type, but I am back to Swif101. The documentation says:

  • Value Types: The data will be used in code across multiple threads.
  • Reference Types: You want to create shared, mutable state

Aren't reference types also shared across multiple threads? What's the difference in these two lines?

解决方案

As others have pointed out, reference types always pass a pointer to the object, which is ideal where you want a "shared, mutable state" (as that document you referenced said). Clearly, though, if you're mutating/accessing a reference type across multiple threads, make sure to synchronize your access to it (via a dedicated serial queue, the reader-writer pattern, locks, etc.).

Value types are a little more complicated, though. Yes, as the others have pointed out, if you pass a value type as a parameter to a method that then does something on another thread, you're essentially working with a copy of that value type (Josh's note regarding the copy-on-write, notwithstanding). This ensures the integrity of that object passed to the method. That's fine (and has been sufficiently covered by the other answers here).

But it gets more complicated when you are dealing with closures. Consider, for example, the following:

struct Person {
    var firstName: String
    var lastName: String
}

var person = Person(firstName: "Rob", lastName: "Ryan")

DispatchQueue.global().async {
    Thread.sleep(forTimeInterval: 1)
    print("1: \(person)")
}

person.firstName = "Rachel"
Thread.sleep(forTimeInterval: 2)
person.lastName = "Moore"
print("2: \(person)")

Obviously, you wouldn't generally sleep, but I'm doing this to illustrate the point: Namely, even though we're dealing with a value type and multiple threads, the person you reference in the closure is the same instance as you're dealing with on the main thread (or whatever thread this was running on), not a copy of it. If you're dealing with a mutable object, that's not thread-safe.

I've contrived this example to illustrate this point, where the print statement inside the closure above will report "Rachel Ryan", effectively showing the state of the Person value type in an inconsistent state.

With closures using value types, if you want to enjoy value semantics, you have to change that async call to use a separate variable:

let separatePerson = person
queue.async {
    Thread.sleep(forTimeInterval: 1)
    print("1: \(separatePerson)")
}

Or, even easier, use a "capture list", which indicates what value type variables should be captured by the closure:

queue.async { [person] in
    Thread.sleep(forTimeInterval: 1)
    print("1: \(person)")
}

With either of these examples, you're now enjoying value semantics, copying the object, and the print statement will correctly report "Rob Ryan" even though the original person object is being mutated on another thread.

So, if you are dealing with value types and closures, value types can be shared across threads unless you explicitly use capture list (or something equivalent) in order to enjoy value semantics (i.e. copying the object as needed).

这篇关于苹果对多线程的引用和值类型的描述的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆