我正在研究.net中的多线程,我坚持这个例子:
class Program
{
public static ConcurrentDictionary<string, int> Channels;
static void Main(string[] args)
{
Channels = new ConcurrentDictionary<string, int>();
Thread adding = new Thread(AddItem);
Thread reading = new Thread(CountItems);
reading.Start();
adding.Start();
Console.ReadKey();
}
public static void AddItem()
{
int i = 0;
while (true)
{
i++;
Channels.TryAdd(i.ToString(), i);
Console.WriteLine(i + ": Item Added");
Thread.Sleep(2000);
}
}
public static void CountItems()
{
while (true)
{
Console.WriteLine(Channels.Count);
Thread.Sleep(3000);
}
}
}
输出是:
1: Item Added
1
2: Item Added
2
3: Item Added
4: Item Added
4
问题是,为什么这两个线程在没有Lock的情况下工作?不应该是那个阅读线程不应该看到变化吗?
答案 0 :(得分:1)
首先,有一个锁,但它在ConcurrentDictionary
中接下来,检查一下锁的含义。这意味着没有2个线程可以进入锁定,在这种情况下,在确切的时间瞬间访问字典。
这并不意味着每个线程都看不到其他线程可以看到的内容。两个线程都看到完全相同的对象引用,因此两者都访问相同的数据,而不是完全相同的时间。锁只在Count或TryAdd的调用中。
要了解锁定的重要性,请查看最简单的示例:
i = i + 1;
这看起来像1个语句,但它将被编译为许多机器代码指令,如下所示:
当你有多个线程时,这些机器插件是交错在一起的,而不是C#。为什么这很重要?好吧,如果您有2个线程同时执行上述语句,那么可能会发生这种情况:
想象我= 42
所以,有2次尝试增加i,但结果是它只增加了一次。
现在想象一下字典类中可能会发生什么 - 可能还有更多只是增加一个计数器 - 如果2个线程同时执行某些操作,则无法确定会发生什么。
现在,如果你这样做:
lock (someObject)
{
i = i + 1;
}
这意味着{}同时只允许1个线程。如果一个线程在{}中,而其他线程想要进入,它就会等到第一个线程完成。
答案 1 :(得分:0)
问题是,为什么这两个线程没有锁定?
ConcurrentDictionary有自己的内部锁。
不应该是阅读线程不应该看到变化吗?
为什么呢?两个线程都使用相同的静态字典实例。
<强>更新强>
如果您将ConcurrentDictionary
更改为Dictionary
,则输出将保持不变。你应该非常幸运(或者说不幸的是)用这么简单的应用程序来解决一些问题。但是,如果你从许多不同的线程使用Dictionary,特别是如果这些线程修改了字典,你就不能确定你不会破坏内部字典数据或得到一些奇怪的例外。