根据该网站的使用MPI::COMM_WORLD.Send(...)
是线程安全的。 然而,在我的应用程序经常(并不总是)陷入死锁或获得分割故障。 围的每个呼叫MPI::COMM_WORLD
方法与mutex.lock()
和mutex.unlock()
一致地去除死锁以及段错误。
这是我如何创建线程:
const auto communicator = std::make_shared<Communicator>();
std::vector<std::future<size_t>> handles;
for ( size_t i = 0; i < n; ++i )
{
handles.push_back(std::async(std::launch::async, foo, communicator));
}
for ( size_t i = 0; i < n; ++i )
{
handles[i].get();
}
Communicator
是具有一个类std::mutex
构件和排他地调用的方法,如MPI::COMM_WORLD.Send()
和MPI::COMM_WORLD.Recv()
我不使用发送/ MPI与接收的任何其他方法。 foo
需要const std::shared_ptr<Commmunicator> &
作为参数。
我的问题:是由MPI与创建的线程不兼容答应线程安全std::async
?
在MPI线程安全不工作的开箱。 首先,你必须确保你的实现实际上支持多线程使MPI调用一次。 随着一些MPI实现,例如开放MPI,这需要与在构建时的特殊选项来配置库。 然后,你要告诉MPI在适当的线程支持级别初始化。 目前,MPI标准定义了四个级别的线程支持:
-
MPI_THREAD_SINGLE
-意味着用户代码是单线程。 这是在其中,如果MPI被初始化默认级别MPI_Init()
被使用; -
MPI_THREAD_FUNNELED
-意味着用户代码是多线程的,但只有主线程使MPI调用。 主线程是初始化的MPI库中的一个; -
MPI_THREAD_SERIALIZED
-意味着用户代码是多线程的,但调用的MPI库序列化; -
MPI_THREAD_MULTIPLE
-意味着用户代码是多线程的,所有线程可以使MPI在任何时间,没有任何同步调用。
为了与线程支持初始化MPI,一个必须使用MPI_Init_thread()
代替MPI_Init()
int provided;
MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
if (provided < MPI_THREAD_MULTIPLE)
{
printf("ERROR: The MPI library does not have full thread support\n");
MPI_Abort(MPI_COMM_WORLD, 1);
}
等效代码与废弃 (以及从MPI-3处移除)C ++绑定:
int provided = MPI::Init_thread(argc, argv, MPI::THREAD_MULTIPLE);
if (provided < MPI::THREAD_MULTIPLE)
{
printf("ERROR: The MPI library does not have full thread support\n");
MPI::COMM_WORLD.Abort(1);
}
线程的支持水平进行排序是这样的: MPI_THREAD_SINGLE
< MPI_THREAD_FUNNELED
< MPI_THREAD_SERIALIZED
< MPI_THREAD_MULTIPLE
,所以任何其他规定的水平,从不同MPI_THREAD_MULTIPLE
将有数值低-这就是为什么if (...)
上面的代码是这么写的。
MPI_Init(&argc, &argv)
相当于MPI_Init_thread(&argc, &argv, MPI_THREAD_SINGLE, &provided)
。 实现不需要以所请求的电平准确地初始化-而它们可以在任何其它电平(更高或更低),这是在返回的初始化provided
输出参数。
欲了解更多信息-请参见MPI标准的§12.4,免费提供在这里 。
对于大多数MPI实现,在一级线程支持MPI_THREAD_SINGLE
实际上等同于在一级提供MPI_THREAD_SERIALIZED
-你在你的情况下,观察什么。
既然你没有指定过您使用的MPI实现,来了一个方便的名单。
我已经说过,开放MPI有,以便能够支持具备适当的标志来编译MPI_THREAD_MULTIPLE
。 但还有另一种追赶 - 它的InfiniBand组件不是线程安全的,因此开在满线支撑位初始化MPI不会使用本地InfiniBand通信。
英特尔MPI有两种不同的口味 - 一种带有一个不完全多线程的支持。 多线程的支持是通过将启用-mt_mpi
选项将MPI编译器的包装,其能够与MT版本的链接。 如果支持OpenMP或autoparalleliser启用该选项也在暗示。 我不知道如何当启用全螺纹支持IMPI采用InfiniBand驱动程序工作。
MPICH(2)不支持InfiniBand的,因此它是线程安全的,可能是最新版本提供MPI_THREAD_MULTIPLE
支持开箱即用。
MVAPICH是英特尔MPI建立的基础,它支持的InfiniBand。 我不知道用InfiniBand的一台机器上使用时,它在全螺纹支撑位的行为。
因为很多计算集群的时下使用InfiniBand结构有关多线程支持InfiniBand的音符是很重要的。 用(在IB部件openib
开放MPI BTL)禁用,最MPI实现切换到另一个协议,例如TCP / IP( tcp
开放MPI BTL),这导致更慢和更潜的通信。
有四个级别MPI线程安全的,不是所有的人每隔实现支持:MPI_THREAD_SINGLE,MPI_THREAD_FUNNELED,MPI_THREAD_SERIALIZED和MPI_THREAD_MULTIPLE。 最后一个,它允许一个进程有多个线程可以同时调用MPI函数,可能是一个你感兴趣的。所以,首先,你需要确保你的实现支持MPI_THREAD_SERIALIZED。
线程安全的要求的水平必须要在通话中指定MPI_Init_thread
。 你叫后MPI_Init_thread
你应该能够安全地调用MPI函数升压(POSIX)线程在自己创建的。