public partial class Program
{
static int numberOfOddNumbers = 0;
static int numberOfEvenNumbers = 0;
const int N = 10000000;
static int[] numbers = new int[N];
static int low = 0; static int up = N - 1;
static void CalculateLow()
{
while (low < up)
{
if (numbers[low] % 2 == 0)
Interlocked.Increment(ref numberOfEvenNumbers);
else
Interlocked.Increment(ref numberOfOddNumbers);
low++;
}
}
static void CalculateUp()
{
while (up > low)
{
if (numbers[up] % 2 == 0)
Interlocked.Increment(ref numberOfEvenNumbers);
else
Interlocked.Increment(ref numberOfOddNumbers);
up--;
}
}
public static void Main(string[] args)
{
for (int i = 0; i < N; i++)
{
numbers[i] = i;
}
Thread t1 = new Thread(CalculateLow);
t1.IsBackground = true;
t1.Start();
Thread t2 = new Thread(CalculateUp);
t2.IsBackground = true;
t2.Start();
Console.WriteLine(t1.IsAlive);
t1.Join();
Console.WriteLine(t1.IsAlive);
t2.Join();
Console.WriteLine($"Odd numbers {numberOfOddNumbers}, " +
$"even numbers: { numberOfEvenNumbers}");
}
}
所以我的问题是我试图在数组中找到偶数和奇数,因此我知道结果,它应该始终是变量
N
。
但是在某些情况下,奇数和偶数相加可能是
N+1
!
由于我在 while
循环条件下有共享变量(上、下),我很好奇这是否是问题的根源?另外,我不确定在没有锁或互锁的情况下增加或减少公共变量是否安全。
所以你似乎意识到了
Interlocked.Increment
,因为你已经使用过它多次了。不要手动递增和递减 low
和 up
,而是对它们使用相同的互锁操作。
实际上更糟糕,因为不能保证编译器实际上会插入一个变量来读取其他线程对这些变量所做的更新。毕竟,从
CalculateLow
的角度来看,up
永远不会改变,因此可以读取一次并存储为寄存器。
这就是
Interlocked.Read
的用武之地,强制读取(原子)变量。
因此,将所有这些结合在一起,您的代码可以更正为:
static void CalculateLow()
{
while (true)
{
int lowCopy = Interlocked.Read(ref low),
upCopy = Interlocked.Read(ref up);
if (lowCopy < upCopy) break;
if (numbers[lowCopy] % 2 == 0)
Interlocked.Increment(ref numberOfEvenNumbers);
else
Interlocked.Increment(ref numberOfOddNumbers);
Interlocked.Increment(ref low);
}
}