如果我没有在创建线程的线程上显式访问它们,是否可以安全地操作在线程之外创建的对象?
我正在开发可可软件,为了在大量数据导入(核心数据)期间保持GUI响应速度,我需要在主线程之外运行导入。
即使我在线程运行时未显式访问那些对象,即使我在主线程中创建了这些对象而不使用锁也可以安全访问这些对象。
解决方案
回答
使用锁的全部目的是确保两个线程不会尝试访问同一资源。如果可以通过其他某种机制保证这样做,那就去做吧。
回答
即使是安全的,但在线程之间使用共享数据而不同步对这些字段的访问也不是最佳实践。哪个线程创建对象无关紧要,但是如果有多个执行行(线程/进程)同时访问该对象,则可能会导致数据不一致。
如果我们完全确定只有一个线程可以访问该对象,那么不同步访问将是安全的。即使那样,我还是希望现在就在代码中进行同步,而不是等到以后应用程序中的更改使第二个线程共享同一数据而不必担心同步访问时再进行同步。
回答
是的,这很安全。一个很常见的模式是创建一个对象,然后将其添加到队列或者其他集合中。第二个"消费者"线程从队列中取出项目并对其进行处理。在这里,我们需要同步队列,而不是添加到队列的对象。
仅同步所有内容并希望达到最佳效果不是一个好主意。我们将需要非常仔细地考虑设计以及哪些线程可以作用于对象上。
回答
是的,我们可以做到,这将是安全的
...
直到第二位程序员来了,并且不理解我们所做的相同假设。第二个(或者第3个,第4个,第5个……)程序员可能会以一种不安全的方式(在创建者线程中)开始使用该对象。造成的问题可能非常微妙,难以追踪。仅出于这个原因,并且因为它很想在多个线程中使用该对象,所以我将使对象线程安全。
为了澄清,(感谢那些发表评论的人):
"线程安全"是指以编程方式设计一种避免线程问题的方案。我并不一定意味着围绕对象设计一个锁定方案。我们可以找到一种使用语言的方法,以使其在创建者线程中使用该对象非法(或者非常困难)。例如,将创建者线程中的范围限制为创建对象的代码块。创建完成后,将对象传递给用户线程,确保创建者线程不再引用该对象。
例如,在C ++中
void CreateObject() { Object* sharedObj = new Object(); PassObjectToUsingThread( sharedObj); // this function would be system dependent }
然后,在创建线程中,我们将无法在创建对象后再访问该对象,将责任转移给使用线程。
回答
要考虑的两件事是:
- 我们必须能够保证在将对象提供给其他线程之前已完全创建和初始化该对象。
- 主(GUI)线程必须通过某种机制来检测数据是否已加载并且一切正常。为了线程安全,这将不可避免地涉及某种形式的锁定。
回答
我相信这对由CoreData NSManagedObjectContext管理的NSManagedObjects(或者子类)是不安全的。通常,CoreData可能会对托管对象进行很多棘手的事情,包括在单独的线程中引发与那些对象有关的故障。特别是,[NSManagedObject initWithEntity:insertIntoManagedObjectContext:](OS X 10.5以后为NSManagedObjects指定的初始化程序)不能保证返回的对象可以安全地传递到另一个线程。
在Apple的开发站点上可以很好地证明将CoreData与多个线程一起使用。
回答
使用Core Data,我们应该有一个单独的管理对象上下文用于导入线程,并连接到同一协调器和持久性存储。我们不能简单地将在主线程使用的上下文中创建的对象扔到另一个线程中,并期望它们起作用。此外,我们不能为此自己锁定;我们必须至少酌情锁定对象所在的托管对象上下文。但是,如果这些对象由视图控件绑定,则没有"钩子"可以将上下文锁定添加到其中。
没有免费的午餐。
本·特伦布尔(Ben Trumbull)在2004年底发表在webobjects-dev列表上的一篇精彩文章中,解释了为什么需要使用单独的上下文以及"仅阅读"并不像我们想象的那么简单或者安全的一些原因。 (整个线程很棒。)他正在讨论企业对象框架和WebObjects,但他的建议也完全适用于Core Data。只需在邮件的内容中将" EC"替换为" NSManagedObjectContext",将" EOF"替换为" Core Data"即可。
对于核心数据中的线程之间共享数据(如之前的Enterprise Objects Framework)的解决方案是"不要"。如果我们已进一步考虑,并且确实需要在线程之间共享数据,那么解决方案是将独立的对象图保留在线程隔离的上下文中,并使用来自一个上下文的save通知中的信息来告知其他上下文要重新获取的内容。 -[NSManagedObjectContext refreshObject:mergeChanges:]专为支持此用途而设计。