当加入线程时,是否需要内存屏障?

5

如果线程A生成另一个线程B,其唯一目的是写入变量V,然后等待它终止,那么是否需要内存屏障以确保在线程A上对V进行的后续读取是新鲜的?我不确定终止/加入操作中是否有任何隐式屏障使它们变得多余。

这里有一个例子:

public static T ExecuteWithCustomStackSize<T>
    (Func<T> func, int stackSize)
{
    T result = default(T);

    var thread = new Thread(
        () => 
                {
                    result = func();
                    Thread.MemoryBarrier(); // Required?
                }
        , stackSize);

    thread.Start();
    thread.Join();

    Thread.MemoryBarrier(); // Required?
    return result;
}

以上代码段中的障碍物是否需要一个或多个?

4
我认为不需要任何内存屏障。如果需要,Thread.Join方法就会变得毫无用处,并且很多人会遇到麻烦。Join方法会等待线程完成,这会包括将值赋给变量。 - Despertar
请查看此线程:https://dev59.com/wmw15IYBdhLWcg3wfbzi - Serve Laurijssen
3个回答

4
不,同步机制会生成隐式内存屏障。线程修改的所有数据在线程加入后都将可见。

1
谢谢您的回答。有任何支持这个说法的文档吗? - Ani
1
@Ani: 在这篇文章中:http://www.albahari.com/threading/part4.aspx(大家都已经知道了),他们提到几乎每种同步机制都会生成一个栅栏。虽然他们没有明确提到`Join`,但由于它将调用线程置于与`Monitor.Wait`相同的状态,这是它也应该生成一个栅栏的强烈提示。此外,等待`Task`也被提到。尽管加入线程有点不同,但我认为它应该提供相同的内存排序保证。 - Tudor
我确实读过那个,但不确定是否有定论,因为就像你所说的,它没有明确提到“Join”。 - Ani
@Ani:嗯,我在网上搜索了很久,似乎每个人都忽略了Thread.Join。我甚至尝试查看微软发布的一些旧的.NET库代码,但Join进入了本地代码。我想我唯一能给你的保证就是:“它挂起调用线程,所以它可能在内部使用事件”,“它应该生成一个栅栏,否则join的一般语义将被违反”,“在Java中它生成一个栅栏”。 :)) - Tudor
非常感谢您关注这个问题! - Ani

0

从文档上看,它们似乎不是必需的 -

MemoryBarrier 仅在具有弱内存排序的多处理器系统上需要(例如,使用多个 Intel Itanium 处理器的系统)。

对于大多数目的,C# lock 语句、Visual Basic SyncLock 语句或 Monitor 类提供了更简单的同步数据方式。

由于您正在使用 join 阻塞,因此更不需要使用 MemoryBarrier。


0

你不需要第一个内存屏障。 只需要在访问在另一个线程中被修改的数据之前调用它们。 由于你没有在'thread'内部这样做,所以不需要调用。

如果你打算保留Join调用,可以摆脱第二个。 如果保留第二个调用,可以摆脱Join。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接