我想知道使数据线程安全的“最佳”方法是什么。
具体来说,我需要保护一个跨多个线程的链表——一个线程可能会尝试从中读取数据,而另一个线程可能会从中添加/删除数据,甚至释放整个列表。我一直在阅读有关锁的文章;它们似乎是最常用的方法,但显然它们可能会出现问题(死锁)。我还阅读了有关原子操作以及线程本地存储的信息。
在您看来,我最好的做法是什么?大多数程序员使用的方法是什么,出于什么原因?
我想知道使数据线程安全的“最佳”方法是什么。
具体来说,我需要保护一个跨多个线程的链表——一个线程可能会尝试从中读取数据,而另一个线程可能会从中添加/删除数据,甚至释放整个列表。我一直在阅读有关锁的文章;它们似乎是最常用的方法,但显然它们可能会出现问题(死锁)。我还阅读了有关原子操作以及线程本地存储的信息。
在您看来,我最好的做法是什么?大多数程序员使用的方法是什么,出于什么原因?
一种没有大量使用但相当合理的方法是指定一个专用线程来拥有每个“共享”结构。该线程通常位于(线程安全;-)队列上等待Queue.Queue
工作请求(例如在 Python 中的一个实例)(读取或更改共享结构),包括请求响应的两个(它们将传递自己的队列)准备好时放置的响应)和没有的响应。这种方法完全序列化了对共享资源的所有访问,轻松地重新映射到多进程或分布式架构(在 Python 中几乎是无脑使用multiprocessing
;-),并且只要底层队列对象是一劳永逸的。
它基本上将共享数据结构的地狱变成了消息传递并发架构的天堂。
OTOH,这可能比用锁和c;-)艰难地把它打出来的开销要高一些。
你可以考虑一个不可变的集合。就像 .net 中的字符串如何具有 Replace、Insert 等方法一样。它不会修改字符串而是创建一个新字符串,LinkedList 集合也可以设计为不可变的。实际上,与其他一些集合数据结构相比,LinkedList 以这种方式实现实际上相当简单。
这是讨论不可变集合的博客文章的链接和 .NET 中一些实现的链接。
http://blogs.msdn.com/jaredpar/archive/2009/04/06/immutable-vs-mutable-collection-performance.aspx
永远记住最重要的线程安全规则。彻底了解代码的所有关键部分。这样,就可以像了解您的 ABC 一样了解他们。只有当您在被问到时能够立即识别它们时,您才会知道在哪些区域运行您的线程安全机制。
之后,请记住经验法则:
(我相信其他人可以添加更多。)
从安全的角度来看,“最好”的方法是对整个数据结构加锁,这样一次只有一个线程可以接触到它。
一旦您决定锁定少于整个结构,大概是出于性能原因,这样做的细节是混乱的,并且对于每个数据结构,甚至相同结构的变体都不同。
我的建议是
从对数据结构的全局锁定开始。分析您的程序以查看它是否真的有问题。
如果这是一个问题,请考虑是否有其他方法来分配问题。你能否尽量减少相关数据结构中的数据量,从而不需要如此频繁或长时间地访问它?例如,如果它是一个队列系统,也许您可以为每个线程保留一个本地队列,并且仅当本地队列过载或不足时才将事物移入或移出全局队列。
查看旨在帮助减少对您正在做的特定类型的事情的争用的数据结构,并仔细而准确地实施它们,以免在安全方面犯错。对于排队的例子,工作窃取队列可能是你需要的。