C# ThreadStatic + volatile 成员未按预期工作

标签 c# multithreading volatile threadstatic

我正在阅读 tips and tricks发布,我想我会尝试一些我以前从未做过的 C# 东西。因此,下面的代码没有任何实际用途,只是一个“测试函数”,看看会发生什么。

无论如何,我有两个静态私有(private)字段:

private static volatile string staticVolatileTestString = "";
[ThreadStatic]
private static int threadInt = 0;

如您所见,我正在测试 ThreadStaticAttributevolatile 关键字。

无论如何,我有一个看起来像这样的测试方法:

private static string TestThreadStatic() {
    // Firstly I'm creating 10 threads (DEFAULT_TEST_SIZE is 10) and starting them all with an anonymous method
    List<Thread> startedThreads = new List<Thread>();
    for (int i = 0; i < DEFAULT_TEST_SIZE; ++i) {
        Thread t = new Thread(delegate(object o) {
            // The anon method sets a newValue for threadInt and prints the new value to the volatile test string, then waits between 1 and 10 seconds, then prints the value for threadInt to the volatile test string again to confirm that no other thread has changed it
            int newVal = randomNumberGenerator.Next(10, 100);
            staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal;
            threadInt = newVal;
            Thread.Sleep(randomNumberGenerator.Next(1000, 10000));
            staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " finished: " + threadInt;
        });
        t.Start(i);
        startedThreads.Add(t);
    }

    foreach (Thread th in startedThreads) th.Join();

    return staticVolatileTestString;
}

我希望看到从这个函数返回的是这样的输出:

thread 0 setting threadInt to 88
thread 1 setting threadInt to 97
thread 2 setting threadInt to 11
thread 3 setting threadInt to 84
thread 4 setting threadInt to 67
thread 5 setting threadInt to 46
thread 6 setting threadInt to 94
thread 7 setting threadInt to 60
thread 8 setting threadInt to 11
thread 9 setting threadInt to 81
thread 5 finished: 46
thread 2 finished: 11
thread 4 finished: 67
thread 3 finished: 84
thread 9 finished: 81
thread 6 finished: 94
thread 7 finished: 60
thread 1 finished: 97
thread 8 finished: 11
thread 0 finished: 88

但是,我得到的是:

thread 0 setting threadInt to 88
thread 4 setting threadInt to 67
thread 6 setting threadInt to 94
thread 7 setting threadInt to 60
thread 8 setting threadInt to 11
thread 9 setting threadInt to 81
thread 5 finished: 46
thread 2 finished: 11
thread 4 finished: 67
thread 3 finished: 84
thread 9 finished: 81
thread 6 finished: 94
thread 7 finished: 60
thread 1 finished: 97
thread 8 finished: 11
thread 0 finished: 88

第二个“一半”的输出符合预期(我想这意味着 ThreadStatic 字段的工作方式与我想象的一样),但似乎一些初始输出已从第一个“一半”中“跳过” .

此外,前“半”中的线程是乱序的,但我知道线程不会在您调用 Start() 后立即运行;但是内部操作系统控件将在它认为合适的时候启动线程。 编辑:不,他们不是,实际上,我只是认为他们是因为我的大脑错过了连续的数字


所以,我的问题是:导致我在输出的前“一半”中丢失几行的问题是什么?例如,行“thread 3 setting threadInt to 84”在哪里?

最佳答案

多个线程同时执行。概念上发生的是这样的:

staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal;
  1. 线程 1 读取 staticVolatileTestString
  2. 线程 2 读取 staticVolatileTestString
  3. 线程 3 读取 staticVolatileTestString
  4. 线程 1 追加内容并写回 staticVolatileTestString
  5. 线程 2 追加内容并写回 staticVolatileTestString
  6. 线程 3 追加内容并写回 staticVolatileTestString

这会导致您的台词丢失。 Volatile 在这里没有帮助;连接字符串的整个过程不是原子的。您需要对这些操作使用锁:

private static object sync = new object();

lock (sync) {
    staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal;
}

关于C# ThreadStatic + volatile 成员未按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10993574/

相关文章:

c# - 在后台线程上初始化时,我的字段应该是易变的吗?

ios - 并发进程执行的同步

c++ - GCC 8 无法编译 make_shared<volatile int>()

c++ - gcc : __sync_lock_test_and_set VS. = 具有可变变量的运算符

c# - 意外的@using 关键字

c# - 手动加载依赖程序集

c# - 通用嵌套类型中的 typeof(T)

c# - 在 TFS 中,如何使用查询 (C#) 查找测试套件中的所有测试用例?

swift - 从 Async F# 在主线程上执行代码

c++ - 编译器是否允许常量折叠本地 volatile ?