C#多线程和重新排序-what';It’这里正在进行
本文关键字:It 正在进行 这里 -what 多线程 新排序 排序 | 更新日期: 2024-09-26 04:49:26
我有以下代码:
public static System.Int32 i = 0;
static void Main(string[] args)
{
new Thread(Worker1) { IsBackground = true }.Start();
new Thread(Worker2) { IsBackground = true }.Start();
Console.WriteLine("Running... Press enter to quit");
Console.ReadLine();
}
static void Worker1(object _)
{
while (true)
{
var oldValue = i;
i++;
var newValue = i;
if (newValue < oldValue)
Console.WriteLine("{2}, i++ went backwards! oldValue={0}, newValue={1}!", oldValue, newValue, DateTime.Now.ToString("HH:MM:ss.fff"));
}
}
static void Worker2(object _)
{
while (true)
{
i++;
}
}
运行时,它会产生如下输出:
17:08:17.020, i++ went backwards! oldValue=6124653, newValue=6113984!
17:08:17.057, i++ went backwards! oldValue=18764535, newValue=18752368!
17:08:17.086, i++ went backwards! oldValue=27236177, newValue=27236176!
17:08:17.087, i++ went backwards! oldValue=27550457, newValue=27535008!
17:08:17.130, i++ went backwards! oldValue=40251349, newValue=40235492!
17:08:17.137, i++ went backwards! oldValue=42339974, newValue=42323786!
17:08:17.142, i++ went backwards! oldValue=43828602, newValue=43828436!
17:08:17.149, i++ went backwards! oldValue=45969702, newValue=45959111!
17:08:17.158, i++ went backwards! oldValue=48705847, newValue=48705549!
17:08:17.230, i++ went backwards! oldValue=71199684, newValue=71199674!
注意:这是在四核i7上运行的,在Windows7上有超线程
据我所知,要么:
Thread2在读取oldValue和newValue之间增加了大约40亿次(但不完全!)。考虑到上面的数字和时间,我在接下来的2秒内中10次彩票的几率似乎更高。
CPU和编译器正在进行一些重新排序。。。这似乎是合乎逻辑的解释,但我不太清楚到底是什么操作导致了这种情况?
有人能解释一下吗?
为了澄清这个问题:作为教育练习的一部分,我有意寻找能够重现记忆重新排序错误的代码。有很多方法可以解决这个问题,我主要感兴趣的是分析发生了什么。
@Tymek在下面给出了答案(现在已经指出了,显而易见)。
你肯定知道i++不是原子的,看起来真的像这样:
- 从存储器向寄存器读取CCD_ 1
- 递增寄存器
- 将寄存器写入CCD_ 2所在的存储器
- 返回
i
该序列可以在任何阶段中断。
考虑到这一点,产生厄运信息的一个案例如下:
- 设
i
为100
- 线程2读取
100
并被中断 - 线程1执行一段时间并将i递增到120(几个完整循环),然后将
i
读取为120
,读取i
并将其递增到i
0,并在var newValue = i;
之前中断 - 线程2将
i
递增到101
并被中断 - 线程一运行并执行
var newValue = i
<-这将在CCD_ 15中读取
现在oldValue
是120
,newValue
是101
。
如果你希望旧值的赋值、i的递增和新值的赋值"同时"发生,你应该使用锁。只需引入一个私有变量,然后对其使用锁即可。这看起来像:
lock(lockObj)
{
var oldValue = i;
i++;
var newValue = i;
}
为了避免这个问题,可以使用Interlocked。增量它将确保操作是原子的和线程安全的。当你使用i++时,它不是原子的,它的值可以在递增过程中更改(例如从另一个线程)。