提问者:小点点

多线程环境下无并发数据访问的数据一致性


当没有锁定和除父线程外没有并发数据访问时,内存屏障是否确保跨线程的数据一致性?

下面是我的场景:

  • 主线程开始处理一组全局数据,然后
  • 此主线程启动几个子线程并等待它们完成
  • 每个子线程在全局数据集的不同子集上工作(要么对象实例完全不同,要么线程在对象内存的不同最终成员/部分上工作)==>; 每个内存地址最多由一个子线程读/写
  • 主线程使用子线程修改的全局数据集继续工作

我希望确保:

  • 我。 每个子线程看到一个新鲜/清洁状态(也就是说,在启动子线程之前执行的(来自主线程的)所有写操作都被子线程看到)
  • II。 在恢复时,主线程会看到新鲜/清洁状态(也就是说,主线程会看到子线程执行的(来自子线程的)所有写操作)

下面是我编写代码的计划:

    public class State
    {
        public string Data1 { get; set; }
        public string Data2 { get; set; }

        public int[] SomeArray { get; set; }
    }

    static void Main(string[] args)
    {
        var state = new State();

        state.Data1 = "Initial Data1 From MainThread";
        state.Data2 = "Initial Data2 From MainThread";
        state.SomeArray = new []{ 0, -1, -2 };
        Thread.MemoryBarrier();//TMB 0.1 Force storing all written value in order to be visible for the child threads

        var thread1 = new Thread(o =>
        {
            //Assert.AreEqual("Initial Data1 From MainThread", input.Data1);
            Thread.MemoryBarrier();//TMB 1.1 Force read value pushed from mainThread
            var input = (State)o;

            Assert.AreEqual("Initial Data1 From MainThread", input.Data1);
            input.Data1 = "Modified by Thread 1";
            input.SomeArray[1] = 11;

            Thread.MemoryBarrier();//TMB 1.2 Force storing all written value in order to be visible for the Main threads
        });
        var thread2 = new Thread(o =>
        {
            //Assert.AreEqual("Initial Data2 From MainThread", input.Data2);
            Thread.MemoryBarrier();//1.1 Force read value pushed from mainThread
            var input = (State)o;

            Assert.AreEqual("Initial Data2 From MainThread", input.Data2);
            input.Data2 = "Modified by Thread 2";
            input.SomeArray[2] = 22;

            Thread.MemoryBarrier();//TMB 1.2 Force storing all written value in order to be visible for the Main threads
        });


        thread1.Start(state);
        thread2.Start(state);

        thread1.Join();
        thread2.Join();

        //Assert.AreEqual("Modified by Thread 1", state.Data1);
        //Assert.AreEqual("Modified by Thread 2", state.Data2);

        Thread.MemoryBarrier();//TMB 0.1 Force retrieving all written value from the child threads

        Assert.AreEqual("Modified by Thread 1", state.Data1);
        Assert.AreEqual("Modified by Thread 2", state.Data2);
        Assert.AreEqual(0, state.SomeArray[0]);
        Assert.AreEqual(11, state.SomeArray[1]);
        Assert.AreEqual(22, state.SomeArray[2]);

        Console.WriteLine("Done");
        Console.ReadLine();
    }

这些问题是:

  1. 上面代码上的断言是否始终为真?
  2. 上面代码上的注释断言是否为false?
  3. 这6条线程。记忆障碍是否确保点i。 和二。 上面?
  4. TMB上的评论是否相关?
  5. 它是否依赖于体系结构? (我正在使用两个x86/x64 CPU)

共1个答案

匿名用户

来自are数组线程安全:

我相信,如果每个线程只在数组的单独部分上工作,一切都会很好。 如果您要共享数据(例如,在线程之间通信),那么您将需要某种内存障碍来避免内存模型问题。

。。。

乔恩·斯基特

由此,我相信答案是:

  1. 可能不是。 我相信Thread.join至少需要设置一个内存障碍。 但我找不到记录在案。
  2. 我认为在这种情况下不需要内存障碍来确保一致的行为,因为没有并发线程读取或写入相同的数据。
  3. 不,如果记忆障碍是不必要的,则不需要。
  4. 我相当确信x86和x64具有相同的行为。 如果内存模型被改变了,那么在移植旧代码时就会引起各种各样的问题。 ARM使用较弱的内存模型,但只要您坚持。NET内存模型,它就会很好。

通常,我会提倡一种稍微偏执的方法,代码可能会更改,并发问题可能很难检测到。 一些经验法则:

  • 如果可能,首选不变性
  • 尽可能使用现有构造(Parallel.For,ConcurrentCollections,Task.Run)
  • 读取或写入共享字段时使用联锁。
  • 如果其他方法不适用,则使用lock,但将临界部分保持得尽可能小,最好运行有限数量的代码。