Swift - 惰性变量是否线程安全?

10
也许这个问题需要一些背景信息。
我正在使用Core Data工作在我的持久层,并发现Core Data不是线程安全的,因此需要将NSManagedObjectContext限制在每个线程中。
因此,我的方法是创建自定义后台线程NSManagedObjectContext来执行获取、保存等操作,同时创建主线程NSManagedObjectContext来从获取的NSManagedObjectId中获取NSManagedObject并将其传递给调用者方法。
默认情况下,Xcode为所有NSManagedObjectContext、NSManagedObjectModel等生成与Core Data相关的模板代码,使用lazy var。
所以我的问题是是否要使用lazy var实例化方法来创建NSManagedObjectContext,前提是lazy var会为每个试图访问它的线程初始化一个对象(不是线程安全的?)
或者
在每个线程中为NSManagedObjectContext声明单独的变量,并使所有与线程相关的方法引用两个不同的NSManagedObjectContext,前提是lazy var是线程安全的,并且只有在访问它时才创建一次,不管线程如何。谢谢!编辑:任何遇到Core Data并发问题的人,this article都提供了一个非常好的设计模式,正如Aaron在下面的评论中指出的那样!
1个回答

30

来自《Swift编程语言:属性》(The Swift Programming Language: Properties):

如果一个被标记为lazy的属性被多个线程同时访问,并且该属性尚未初始化,则不能保证该属性仅会被初始化一次。

lazy var不是线程安全的。您可以使用以下方法实现线程安全:

  • dispatch_once(应用程序生命周期内仅运行一次)
  • 一个常量(let)
  • 嵌套结构体模式(通常用于单例)

(有关一些示例,请参见此问题

您也可以使用NSRecursiveLock进行自己的锁定,但这可能不如dispatch_once高效。


1
不,这意味着如果两个线程同时尝试访问它,则第二个线程可能会收到一个部分初始化的对象。由于NSManagedObjectContext不是线程安全的,因此您不应该从不同的线程访问它。 - Aaron Brager
1
好的,所以我应该为我想要使用的每个线程声明不同的 NSManagedObjectContext 实例,并强制在后台线程执行的方法仅访问先前在相同后台线程中创建的 NSManagedObjectContext。谢谢。 - Daniel Shin
@AaronBrager 很抱歉,我的意思不是说你提供的选项有什么问题。只是因为在 iOS 应用程序中无法避免多线程,所以我本来期望它能够直接支持线程安全。 - hennes
4
小提示: dispatch_once 使用静态谓词,因此它将在应用程序的生命周期内只运行一次。如果懒惰变量是实例变量,则可能需要使用您提到的其他锁定技术或者 dispatch semaphore。 - hennes
1
@hennes 锁定操作会带来计算成本,因此将其设置为默认选项是没有意义的。不过,将其作为选项(例如lazy threadsafe var)会很好。此外,许多var通常是UIKit对象,大部分情况下必须在主线程上访问。然而,更Swift的解决方案是传递不可变值类型,这样就不需要锁定了。 - Aaron Brager
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接