如果我没有在创建它们的线程上显式访问它们,那么操作我在线程外部创建的对象是否安全?

时间:2008-09-15 21:27:03

标签: cocoa multithreading macos core-data

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

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

7 个答案:

答案 0 :(得分:4)

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

没有免费的午餐。

Ben Trumbull解释了为什么你需要使用一个单独的上下文,以及为什么“只是阅读”并不像你想象的那样简单或安全,this great post from late 2004 on the webobjects-dev list。 (整个线程很棒。)他正在讨论Enterprise Objects Framework和WebObjects,但他的建议也完全适用于Core Data。只需将“EC”替换为“NSManagedObjectContext”,将“EOF”替换为“核心数据”。

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

答案 1 :(得分:1)

我认为这对于由CoreData NSManagedObjectContext管理的NSManagedObjects(或子类)来说是安全。通常,CoreData可以使用托管对象的状态执行许多棘手的操作,包括在单独的线程中触发与这些对象相关的故障。特别是,[NSManagedObject initWithEntity:insertIntoManagedObjectContext:](从OS X 10.5开始,NSManagedObjects的指定初始化程序)不保证返回的对象可以安全地传递给其他线程。

在Apple的dev site中详细记录了将CoreData与多个线程一起使用。

答案 2 :(得分:0)

使用锁的重点是确保两个线程不会尝试访问同一资源。如果你能通过其他机制保证,那就去吧。

答案 3 :(得分:0)

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

如果您完全确定只有一个线程可以访问此对象,那么不能同步访问是安全的。即便如此,我还是宁愿在我的代码中同步,而不是等到应用程序中的更改使第二个线程共享相同的数据而不用担心同步访问。

答案 4 :(得分:0)

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

同步一切并希望获得最佳效果并不是一个好主意。您需要仔细考虑您的设计以及确切的线程可以对您的对象起作用。

答案 5 :(得分:0)

是的,你可以做到,这将是安全的

... 直到第二个程序员到来并且不理解你所做的相同假设。第二个(或第3个,第4个,第5个......)程序员可能会以非安全的方式(在创建者线程中)开始使用该对象。引起的问题可能非常微妙,难以追查。仅仅因为这个原因,并且因为它很容易在多个线程中使用这个对象,我会使对象线程安全。

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

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

例如,在C ++中

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

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

答案 6 :(得分:0)

需要考虑的两件事是:

  • 您必须能够保证在对象可供其他线程使用之前完全创建和初始化该对象。
  • 主(GUI)线程必须有一些机制来检测数据是否已加载且一切正常。为了保证线程安全,这将不可避免地涉及某种锁定。