3

我正在开发一个可可软件,为了在大量数据导入(核心数据)期间保持 GUI 响应,我需要在主线程之外运行导入。

如果我在线程运行时没有显式访问这些对象,即使我在主线程中创建它们而不使用锁,访问这些对象是否安全。

4

7 回答 7

4

使用 Core Data,您应该有一个单独的托管对象上下文用于您的导入线程,连接到相同的协调器和持久存储。您不能简单地将在主线程使用的上下文中创建的对象扔到另一个线程中并期望它们工作。此外,您不能为此进行自己的锁定;您必须至少锁定对象所在的托管对象上下文,视情况而定。但是,如果这些对象被您的视图绑定到控件,则没有“钩子”可以添加上下文的锁定。

没有免费的午餐。

Ben Trumbull在 2004 年末 webobjects-dev list 上的这篇精彩文章中解释了您需要使用单独上下文的一些原因,以及为什么“仅阅读”不像您想象的那么简单或安全。(整个线程都很棒。)他正在讨论 Enterprise Objects Framework 和 WebObjects,但他的建议也完全适用于 Core Data。只需在他的消息内容中将“EC”替换为“NSManagedObjectContext”,将“EOF”替换为“Core Data”即可。

Core Data 中线程间共享数据问题的解决方案,就像之前的 Enterprise Objects Framework 一样,是“不要”。如果您进一步考虑过,并且确实确实必须在线程之间共享数据,那么解决方案是将独立的对象图保存在线程隔离的上下文中,并使用来自一个上下文的保存通知中的信息来告诉其他上下文要重新获取的内容。 -[NSManagedObjectContext refreshObject:mergeChanges:]专为支持此用途而设计。

于 2008-09-16T00:27:08.787 回答
1

我相信这对于由 CoreData NSManagedObjectContext 管理的 NSManagedObjects(或子类)是不安全的一般来说,CoreData 可能会对托管对象的状态做很多棘手的事情,包括在单独的线程中触发与这些对象相关的错误。特别是,[NSManagedObject initWithEntity:insertIntoManagedObjectContext:](从 OS X 10.5 开始,NSManagedObjects 的指定初始化程序)不保证返回的对象可以安全地传递给其他线程。

在 Apple 的开发网站上对使用多线程的 CoreData 进行了详细记录。

于 2008-09-15T23:36:28.813 回答
0

使用锁的全部意义在于确保两个线程不会尝试访问同一个资源。如果您可以通过其他机制保证这一点,那就去吧。

于 2008-09-15T21:31:23.123 回答
0

即使它是安全的,但在不同步对这些字段的访问的情况下使用线程之间的共享数据并不是最佳实践。哪个线程创建了对象并不重要,但如果有多个执行行(线程/进程)同时访问该对象,则可能导致数据不一致。

如果您绝对确定只有一个线程会访问该对象,那么不同步访问是安全的。即便如此,我宁愿现在就在我的代码中进行同步,而不是等到稍后应用程序中的更改使第二个线程共享相同的数据而不用担心同步访问。

于 2008-09-15T21:34:34.207 回答
0

是的,它是安全的。一种非常常见的模式是创建一个对象,然后将其添加到队列或其他集合中。第二个“消费者”线程从队列中获取项目并对其进行处理。在这里,您需要同步队列,但不需要同步添加到队列中的对象。

只同步所有内容并希望获得最好的结果并不是一个好主意。您需要非常仔细地考虑您的设计以及哪些线程可以作用于您的对象。

于 2008-09-15T21:39:24.710 回答
0

是的,您可以这样做,这将是安全的

...直到第二个程序员出现并且不理解您所做的相同假设。第二个(或第三个、第四个、第五个……)程序员可能会以非安全的方式(在创建者线程中)开始使用对象。造成的问题可能非常微妙且难以追踪。仅出于这个原因,并且因为在多个线程中使用这个对象非常诱人,我会让对象线程安全。

澄清一下,(感谢那些留下评论的人):

“线程安全”是指以编程方式设计一种方案来避免线程问题。我不一定是指围绕您的对象设计一个锁定方案。您可以在您的语言中找到一种方法,使在创建者线程中使用该对象成为非法(或非常困难)。例如,在创建者线程中将范围限制为创建对象的代码块。创建后,将对象传递给用户线程,确保创建者线程不再引用它。

例如,在 C++ 中

void CreateObject()
{
    Object* sharedObj = new Object();
    PassObjectToUsingThread( sharedObj); // this function would be system dependent
}

然后在您的创建线程中,您在创建对象后不再有权访问该对象,责任将传递给使用线程。

于 2008-09-15T21:40:55.770 回答
0

需要考虑的两件事是:

  • 您必须能够保证对象在其他线程可用之前已完全创建和初始化。
  • 必须有某种机制让主 (GUI) 线程检测到数据已加载并且一切正常。为了线程安全,这将不可避免地涉及某种锁定。
于 2008-09-15T21:45:59.547 回答