我主动向我的团队提出了一个由指令重排而引入bug的情况,然而我对CPU、CLR和JIT的理解相当外行,我没能举出一个好例子。
下面我展示的是我想出的最好的办法,请大家从这里开始看代码片段来理解我说的是什么。
最主要的一点是在 thread2 的 如果声明如果我在线程1或线程2中手动重新排列指令->打印就会发生(即使你在线程2中交换c.x和c.y的读数,它也会因为竞赛条件而打印)。
我的想法是通过将相距较远的变量变成整数来强制重新安排x和z的写入,认为由于8个字节字的大小,它可以在一个cpu周期内同时写入它们,而不是3个周期写入4->;8->;4个字节。(我知道这实际上不是3个cpu周期,很不幸,我不懂汇编。) 我甚至在万不得已的情况下尝试把它放在一个结构体中,认为这样可以迫使JIT进行某种优化。
如果有任何帮助,我将非常感激,因为我非常渴望让它工作。(我也试着按照Joseph Albahari的电子书中的例子去做,但那些都没有用,这就是为什么我试着做一个更复杂的例子)。我也没有忘记用Release for x64指令集编译。
代码。
public class Program
{
public static void Main()
{
var stopWatch = new Stopwatch();
for (var i = 0; i < 100000000; i++)
{
var delegates = new MultiTreadingDelegates(i);
Task.Run(delegates.Thread1);
Task.Run(delegates.Thread2);
}
Console.WriteLine("finished");
Console.ReadKey();
}
}
public class MultiTreadingDelegates
{
private int i = 0;
private Container container = new Container();
public MultiTreadingDelegates(int i)
{
this.i = i;
}
public void Thread1()
{
container.X = 10000000;
container.Z = 6000000000;
container.Y = 20000000;
}
public void Thread2()
{
int y = container.Y;
long z = container.Z;
int x = container.X;
if (x != 0 && z == 0 && y != 0)
{
System.Console.WriteLine($"i = {i}{Environment.NewLine}"
+ $"x = {x}{Environment.NewLine}"
+ $"z = {z}{Environment.NewLine}"
+ $"y = {y}{Environment.NewLine}"
);
}
}
}
public struct Container
{
public int X;
public long Z;
public int Y;
}
我再次感谢大家的帮助。
class Program
{
static void Main(string[] args)
{
Task.Run(DelegatesUsingPetersons.Thread1);
Task.Run(DelegatesUsingPetersons.Thread2).GetAwaiter().GetResult();
}
}
static class DelegatesUsingPetersons
{
private static long x = 0;
private static long y = 0;
private static bool flag1 = false;
private static bool flag2 = false;
public static void Thread1()
{
while (true)
{
flag1 = true;
/*Thread.MemoryBarrier();*/ //Uncomment to fix locking mechanism
if (flag2 == false)
{
x++;
y++;
}
flag1 = false;
}
}
public static void Thread2()
{
long lx = 0;
long ly = 0;
while (true)
{
flag2 = true;
/*Thread.MemoryBarrier();*/ //Uncomment to fix locking mechanism
if (flag1 == false)
{
lx = x;
ly = y;
}
flag2 = false;
if (lx != ly)
{
Console.WriteLine($"lx={lx}, ly={ly} - OMG this cannot happen!");
}
}
}
}
如果你想把它与一个 "更传统 "的工作代码并列,这里是同样的代码,只是没有Mr.Peterson做所有的花哨的算法巫术。
static class DelegatesUsingLock
{
private static long x = 0;
private static long y = 0;
private static object loq = new object();
public static void Thread1()
{
while (true)
{
if (Monitor.TryEnter(loq))
{
x++;
y++;
Monitor.Exit(loq);
}
}
}
public static void Thread2()
{
long lx = 0;
long ly = 0;
while (true)
{
if (Monitor.TryEnter(loq))
{
lx = x;
ly = y;
Monitor.Exit(loq);
}
if (lx != ly)
{
Console.WriteLine($"lx={lx}, ly={ly} - This Never Happens");
}
}
}
}