我已经阅读了一些关于线程安全的内容,但我想了解我需要哪些操作来加锁。
例如,假设我想要一个线程安全队列/如果出队操作将返回第一个元素(如果有的话),我什么时候需要锁?可以说我正在为条目使用抽象链表。
应该锁定写操作吗?还是读书的?或两者?
希望有人可以向我解释或给我一些链接。
我已经阅读了一些关于线程安全的内容,但我想了解我需要哪些操作来加锁。
例如,假设我想要一个线程安全队列/如果出队操作将返回第一个元素(如果有的话),我什么时候需要锁?可以说我正在为条目使用抽象链表。
应该锁定写操作吗?还是读书的?或两者?
希望有人可以向我解释或给我一些链接。
并发场景中的同步是一个非常广泛的话题。本质上,只要两个或多个线程在它们之间具有某种共享状态(计数器、数据结构),并且其中至少一个在读取或来自不同线程的另一个突变的同时改变这种共享状态,结果可能会不一致。在这种情况下,您将需要使用某种形式的同步(其中锁是一种风格)。
现在转到您的问题,执行出队的典型代码如下(伪代码):
if(queue is not empty)
queue.dequeue
它可以由多个线程同时执行。尽管一些队列实现在内部同步了queue is not empty
操作和queue.dequeue
操作,但这还不够,因为执行上述代码的线程可能会在检查和实际出队之间被中断,所以一些线程可能会在到达出队时发现队列为空即使检查返回 true。需要锁定整个序列:
lock(locker)
{
if(queue is not empty)
queue.dequeue
}
请注意,以上内容可能由某些数据结构实现为单线程安全操作,但我只是想在这里说明一点。
我找到的关于锁定和线程的最佳指南是这个页面(这是我在使用锁定和线程时参考的文本):
http://www.albahari.com/threading/
你想要“锁定和线程安全”这一段,但也要阅读其余部分,它写得很好。
锁定的基本规则
锁定需要防止出现不飞行的情况。这可以通过多种方式完成。C#为此提供了很多工具。除了 Concurrent<> 集合类型,如 ConcurrentDictionary、ConcurrentQueue 等,还有 ReaderWriterLockSlim 等。
您可能会发现这个来自 microsoft 的免费 .pdf 文件很有用。它被称为“C# 线程编程简介”
http://research.microsoft.com/pubs/70177/tr-2005-68.pdf
这个有点幽默的接力
http://www.codeproject.com/Articles/114262/6-ways-of-doing-locking-in-NET-Pessimistic-and-opt
作为最简单的经验法则,所有共享的可变数据都需要在您访问它时锁定一个锁。
写入时需要锁,因为您需要确保没有人同时写入相同的字段。
读取时需要加锁,因为另一个线程可能正在写入数据,所以它可能处于不一致的状态。不一致的数据会产生不正确的输出或崩溃。
锁有自己的一系列相关问题,(谷歌为“哲学家就餐”)所以我倾向于尽可能避免使用显式锁。更高级别的构建块,例如 ConcurrentQueue<> 不太容易出错,但您仍应阅读文档。
另一种避免锁定的简单方法是为后台进程制作输入数据的副本。或者更好的是,使用不可变的输入(不能改变的数据)。
有关基本概述,请参阅MSDN:线程同步。有关更详细的介绍,我建议阅读Amazon:Windows 上的并发编程。
您需要锁定受非原子操作影响的对象。
将对象添加到列表 -> 非原子
给一个字节或一个 int -> atomic 赋值