C# 将共享值从多个仿射到一个CPU的线程递增,如何使用++;是三个指令吗?

C# 将共享值从多个仿射到一个CPU的线程递增,如何使用++;是三个指令吗?,c#,multithreading,caching,increment,C#,Multithreading,Caching,Increment,我以为我对这个问题了解很多,但下面的问题让我困惑。我知道,在我运行此功能的特定PC上,“x++”被转换为三条汇编指令(将值从内存移到寄存器、在寄存器中递增、写回内存)。那么,究竟为什么当锁定到单个CPU时,递增值的线程不会在值递增的确切时间被抢占,而是仍在寄存器中,而不会写回内存?有人知道这个问题的答案吗?我猜这完全是运气使然,但我不能确切地证明这一点 我可以通过在VS的反汇编窗口中设置一个断点来消除正确的结果,然后在调试器中执行一些步骤,但这不是证据。不是实心的 [这段代码不是线程安全的。这不

我以为我对这个问题了解很多,但下面的问题让我困惑。我知道,在我运行此功能的特定PC上,“x++”被转换为三条汇编指令(将值从内存移到寄存器、在寄存器中递增、写回内存)。那么,究竟为什么当锁定到单个CPU时,递增值的线程不会在值递增的确切时间被抢占,而是仍在寄存器中,而不会写回内存?有人知道这个问题的答案吗?我猜这完全是运气使然,但我不能确切地证明这一点

我可以通过在VS的反汇编窗口中设置一个断点来消除正确的结果,然后在调试器中执行一些步骤,但这不是证据。不是实心的

[这段代码不是线程安全的。这不是生产代码,它是用于教育目的的。我正试图深入研究这一主题。当然,由于缓存线不存在,我们得到了很多,但仍然--三条指令,甚至没有一条遗漏,这很奇怪!这是一个4核CPU,x64。]

{
    private static int NumberOfThreads = 2;
    private const long IncrementIterations = 1000000000;

    private static int _x;

    static void Main(string[] args)
    {
        Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1;

        Console.WriteLine("Hit Enter");
        Console.ReadLine();

        while (true)
        {
            var barrier = new Barrier(NumberOfThreads);

            _x = 0;

            var threads = new List<Thread>();

            for (int i = 0; i < NumberOfThreads; i++)
            {
                var thread = new Thread(
                    delegate()
                    {
                        barrier.SignalAndWait();

                        for (int j = 0; j < IncrementIterations; j++)
                        {
                            _x++;
                        }
                    });

                thread.Start();

                threads.Add(thread);
            }

            BlockUntilAllThreadsQuit(threads);

            Console.WriteLine(_x);

            Console.WriteLine("Actual increments: " + (IncrementIterations * NumberOfThreads));

            if (_x != (IncrementIterations * NumberOfThreads))
            {
                Console.WriteLine("Observed: " + _x);
                break;
            }
        }

        Console.ReadLine();
    }

    private static void BlockUntilAllThreadsQuit(IEnumerable<Thread> threadsToWaitFor)
    {
        foreach (var thread in threadsToWaitFor)
        {
            thread.Join();
        }
    }
}
{
私有静态int NumberOfThreads=2;
private const long IncrementIterations=100000000;
私有静态int_x;
静态void Main(字符串[]参数)
{
Process.GetCurrentProcess().ProcessorAffinity=(IntPtr)1;
控制台写入线(“点击回车”);
Console.ReadLine();
while(true)
{
var屏障=新屏障(螺纹数);
_x=0;
var threads=newlist();
for(int i=0;i
如果使用带有x字段的类,而不是使用被提升到闭包的x变量,则程序会更清晰。话虽如此,如果线程在执行增量时被抢占,那么增量只能是原子的,特别是在加载完成和存储开始之间。那是一扇很小的窗户。可能会发生一些任意事件,并在该时刻触发任务切换,但可能性不大。请注意,运行循环所需的时间量可能会导致普通计时器滴答事件始终错过神奇的机会窗口

如果您想更好地进行测试,我建议您向循环中添加一点代码,以稍微随机化时间(例如,使用真正的自动变量
randThing
初始化为1,执行以下操作:

if (randthing & 0x20000000) randthing ^= 0x20043251); // Number picked out of hat; a primitive polynomial // would be better. else randthing <<= 1; 如果(随机事件&0x20000000) randthing^=0x20043251);//从帽子里挑出来的号码;本原多项式 //那就更好了。 其他的
randthing如果使用带有x字段的类,而不是使用被提升到闭包的x变量,那么程序会更清晰。话虽如此,如果线程在执行增量时被抢占,那么增量只能是原子的,特别是在加载完成和存储开始之间。那是一扇很小的窗户。可能会发生一些任意事件,并在该时刻触发任务切换,但可能性不大。请注意,运行循环所需的时间量可能会导致普通计时器滴答事件始终错过神奇的机会窗口

如果您想更好地进行测试,我建议您向循环中添加一点代码,以稍微随机化时间(例如,使用真正的自动变量
randThing
初始化为1,执行以下操作:

if (randthing & 0x20000000) randthing ^= 0x20043251); // Number picked out of hat; a primitive polynomial // would be better. else randthing <<= 1; 如果(随机事件&0x20000000) randthing^=0x20043251);//从帽子里挑出来的号码;本原多项式 //那就更好了。 其他的
randthing那么你是在不同的线程上增加
\ux
吗?Matthew,是的。但是它们与一个CPU内核密切相关。当x++是一条汇编指令时,为什么所有线程都运行在一个CPU内核上,这一点并不神秘,但是在这种特殊情况下,我有三条x++指令。当前代码的输出是什么?期望的输出是什么?你能围绕增量发布反汇编指令吗?根据这个()MSDN文章,“一个线程可以在增量或减量操作的中间被抢占——例如,在它已经加载并递增了计数之后,但是在新的值被存储之前。当线程被抢占时,另一个线程可以增加计数,或者终结器线程可以减少计数。”另外,这是发布版还是调试版?你能确认这两种情况都会发生吗?那么你是想在不同的线程上增加
\ux
?Matthew,是的。但是它们与一个CPU内核是相似的