虚假共享和堆栈变量

use*_*947 7 c++ false-sharing

我有一些小但经常使用的函数对象.每个线程都有自己的副本.一切都是静态分配的.副本不共享任何全局或静态数据.我是否需要保护此对象免遭错误共享?

谢谢.编辑:这是一个使用Boost.Threads的玩具程序.字段数据可能会发生错误共享吗?

#include <boost/thread/thread.hpp>

struct Work {
    void operator()() {
        ++data;
    }

    int data;
};

int main() {
    boost::thread_group threads;
    for (int i = 0; i < 10; ++i)
        threads.create_thread(Work());
    threads.join_all();
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*her 6

线程之间的错误共享是当2个或更多线程使用相同的缓存行时.

例如:

struct Work {
    Work( int& d) : data( d ) {}
    void operator()() {
        ++data;
    }

    int& data;
};

int main() {
    int false_sharing[10] = { 0 };
    boost::thread_group threads;
    for (int i = 0; i < 10; ++i)
        threads.create_thread(Work(false_sharing[i]));
    threads.join_all();

    int no_false_sharing[10 * CACHELINE_SIZE_INTS] = { 0 };
    for (int i = 0; i < 10; ++i)
        threads.create_thread(Work(no_false_sharing[i * CACHELINE_SIZE_INTS]));
    threads.join_all();
}
Run Code Online (Sandbox Code Playgroud)

第一个块中的线程确实遭受错误共享.第二个块中的线程没有(由于CACHELINE_SIZE).

堆栈上的数据总是远离其他线程.(例如在windows下,至少有几页).

通过定义函数对象,可以显示错误共享,因为Work在堆内创建的get 实例和此堆空间在线程内部使用.

这可能导致几个Work实例相邻,因此可能导致共享高速缓存行.

但是......你的样本没有意义,因为数据永远不会被触及,因此不必要地引起错误共享.

防止这样的问题最简单的方法是在堆栈上本地复制"共享"数据,然后处理堆栈副本.完成工作后,将其复制回输出var.

例如:

struct Work {
    Work( int& d) : data( d ) {}
    void operator()()
    {
        int tmp = data;
        for( int i = 0; i < lengthy_op; ++i )
           ++tmp;
        data = tmp;
    }

    int& data;
};
Run Code Online (Sandbox Code Playgroud)

这可以防止共享的所有问题.