C++ 使用std::async创建的线程实现MPI发送的线程安全

C++ 使用std::async创建的线程实现MPI发送的线程安全,c++,multithreading,c++11,thread-safety,mpi,C++,Multithreading,C++11,Thread Safety,Mpi,根据,MPI::COMM_WORLD.Send(…)的使用是线程安全的。然而,在我的应用程序中,我经常(并非总是)遇到死锁或分段错误。用mutex.lock()和mutex.unlock()将MPI::COMM_WORLD方法的每个调用封装起来,一致地移除死锁和segfault 这就是我创建线程的方式: const auto communicator = std::make_shared<Communicator>(); std::vector<std::future<s

根据,MPI::COMM_WORLD.Send(…)的使用是线程安全的。然而,在我的应用程序中,我经常(并非总是)遇到死锁或分段错误。用
mutex.lock()
mutex.unlock()
MPI::COMM_WORLD
方法的每个调用封装起来,一致地移除死锁和segfault

这就是我创建线程的方式:

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();
}
const auto communicator=std::make_shared();
向量句柄;
对于(尺寸i=0;i
Communicator
是一个类,它有一个
std::mutex
成员,专门调用方法,如
MPI::COMM\u WORLD.Send()
MPI::COMM\u WORLD.Recv()。我不使用MPI的任何其他发送/接收方法
foo
const std::shared\u ptr&
作为参数


我的问题是:MPI承诺的线程安全性是否与由
std::async
创建的线程不兼容?

MPI中的线程安全性是不现成的。首先,您必须确保您的实现实际上支持同时进行MPI调用的多个线程。对于某些MPI实现,例如OpenMPI,这要求在构建时使用特殊选项配置库。然后,您必须告诉MPI在适当的线程支持级别进行初始化。目前,MPI标准定义了四个级别的线程支持:

  • MPI\u THREAD\u SINGLE
    -表示用户代码是单线程的。如果使用
    MPI\u Init()
    ,则这是初始化MPI的默认级别
  • MPI\u THREAD\u FUNNELED
    -表示用户代码是多线程的,但只有主线程进行MPI调用。主线程是初始化MPI库的线程
  • MPI\u THREAD\u SERIALIZED
    -表示用户代码是多线程的,但对MPI库的调用是序列化的
  • MPI\u THREAD\u MULTIPLE
    -表示用户代码是多线程的,所有线程都可以在任何时候进行MPI调用,而无需任何同步
为了使用线程支持初始化MPI,必须使用
MPI\u Init\u thread()
而不是
MPI\u Init()

int提供;
MPI_Init_线程(&argc,&argv,MPI_线程多个,&provided);
如果(提供

等效代码:<<强>过时< /强>(和从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);
}
int-provided=MPI::Init_-thread(argc、argv、MPI::thread_-MULTIPLE);
如果(提供
线程支持级别的顺序如下:
MPI\u线程单线程
MPI\u线程漏斗状
MPI\u线程序列化
MPI\u线程多线程
,因此任何其他提供的级别,不同于
MPI\u线程多线程
,都具有较低的数值-这就是为什么
if(…)
上面的代码就是这样写的

MPI_Init(&argc,&argv)
相当于
MPI_Init_线程(&argc,&argv,MPI_线程_SINGLE,&provided)
。实现不需要精确地在请求的级别初始化,而是可以在任何其他级别(更高或更低)初始化,该级别在提供的
输出参数中返回

有关更多信息,请参见MPI标准的§12.4,免费提供

对于大多数MPI实现,级别
MPI\u thread\u SINGLE
上的线程支持实际上与级别
MPI\u thread\u SERIALIZED
上提供的线程支持等效-这正是您在案例中观察到的

因为您还没有指定使用哪种MPI实现,所以这里有一个方便的列表

我已经说过,为了支持
MPI\u THREAD\u MULTIPLE
,必须在启用适当标志的情况下编译openmpi。但还有另一个问题——它的InfiniBand组件不是线程安全的,因此OpenMPI在完全线程支持级别初始化时不会使用本机InfiniBand通信

英特尔MPI有两种不同的风格——一种支持全多线程,另一种不支持全多线程。多线程支持是通过将
-mt\u mpi
选项传递给mpi编译器包装器来启用的,该包装器支持与mt版本的链接。如果启用了OpenMP支持或autoparalleliser,也会暗示此选项。我不知道IMPI中的InfiniBand驱动程序在启用全线程支持时是如何工作的

MPICH(2)不支持InfiniBand,因此它是线程安全的,并且可能是最新版本提供了“开箱即用”支持

MVAPICH是构建英特尔MPI的基础,它支持InfiniBand。我不知道在使用InfiniBand的机器上使用时,它在全线程支持级别下的行为


关于多线程InfiniBand支持的注意事项很重要,因为现在很多计算集群都使用InfiniBand结构。禁用IB组件(
openib
BTL in Open MPI)后,大多数MPI实现都会切换到另一个协议,例如TCP/IP(
TCP
BTL in Open MPI),这会导致更慢、更潜在的通信。

MPI线程安全有四个级别,并不是所有的实现都支持它们:MPI_线程单、MPI_线程漏斗、MPI_线程序列化和MPI_线程多。最后一个允许进程具有多个线程,这些线程可以同时调用MPI函数,这可能是您感兴趣的。因此,首先,您需要确保您的实现支持MPI\u THRE
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);
}