混合pthread.h和C ++ 11标准库线程功能是否安全?

时间:2013-03-12 17:26:35

标签: c++ c++11 pthreads mutex

我可以使用pthread_create生成一个帖子并安全地使用std::mutex吗?

我认为如果将std::mutex作为pthread_mutex_t实施,那么它会没问题,但我没有在任何地方看到这些

例如:

#include <pthread.h>
#include <mutex>

namespace {
std::mutex global_lock;
}

void* thread_func(void* vp) {
    // std::mutex used in thread spawned with pthread_create
    std::lock_guard<std::mutex> guard(global_lock);
    // critical section
    return nullptr;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, nullptr, thread_func, nullptr);
    pthread_join(tid, NULL);
}

BTW我正在运行Debian Wheezy。

4 个答案:

答案 0 :(得分:7)

在任何规范中都没有保证它会起作用,但是在使用pthreads作为其唯一真正的线程库的操作系统上的任何C ++实现都可能在C ++线程下使用pthread,因此它可能会起作用。

如果您稍后尝试将代码移植到使用除pthread之外的其他平台,您可能会遇到问题,即使该平台也支持pthreads(例如,windows)。

问题是,为什么要打扰并冒风险呢?如果您使用的是C ++ 11 std::mutex,为什么不使用std::thread

答案 1 :(得分:6)

你可以在我的机器上(Debian也是如此)。但我不确定我是否会称之为安全。

如果你查看我的案例中的相关文件/usr/include/c++/4.7/i486-linux-gnu/bits/gthr-default.h,你会发现pthreads api会有1:1的映射。 <mutex>使用__gthread_mutex_lock进行锁定,该锁定与pthread_mutex_lock完全相同。或者您会看到std::thread声明typedef __gthread_t native_handle_type;

我不知道是否有记录的方法来检查是否使用了pthreads。但gthr-default.h_GLIBCXX_GCC_GTHR_POSIX_H定义为包含守卫,我认为只要定义了此宏,就可以假设您可以将它们混合使用。

编辑:鉴于@Wakely的提示,我会写:

template <typename T>
using strip = typename std::remove_pointer<typename std::decay<T>::type>::type;

static_assert(std::is_same<strip<std::thread::native_handle_type>, pthread_t>::value,
              "libstdc++ doesn't use pthread_t");

答案 2 :(得分:3)

std::threadstd::mutex都有native_handle方法,可让您深入了解给定对象的平台实现。这告诉我标准的线程库旨在与平台实现相得益彰。

旁边std::threadstd::mutex是不同的对象,可以做不同的事情。管理线程并提供跨线程同步。最后,内核完成了繁重的任务。

因此,如果您不担心可移植性,我不明白为什么这应该是一个问题。

顺便说一句,有时您可能需要本机平台实现,以便为您提供平台允许的更丰富的功能集。例如,BSD线程允许不同类型的线程,一些线程库允许您设置新线程的堆栈大小。 C ++线程API是可移植的最低公分母。

答案 3 :(得分:0)

如果你的问题是:我可以随意使用一种互斥体和另一种互斥体之间的自由切换吗?然后答案是否定的(除非所有底层都使用相同的实现,例如pthread_mutex。)

但是,如果您要保护不同的资源组,则可以使用任何一个实现保护任何一组资源。实际上,使用信号量有时会更好(例如,信号量对于定义一个写入锁定很有用,许多锁定用于读取)。

因此,如果您有4组资源,您可以使用:

  • 的std ::互斥,
  • pthread_mutex,
  • 升压::互斥,
  • 信号量。

你不能做的是使用boost :: mutex来访问受信号量保护的数据,反之亦然,或使用std :: mutex来使用受pthread_mutex保护的东西。

作为一个简单的例子,就代码而言,这意味着getter和setter将以这种方式完成:

void set(int new_value)
{
    guard lock(my_mutex);
    m_value = new_value;
}

int get() const
{
    guard lock(my_mutex);
    return m_value;
}

这两个函数使用相同的互斥锁(此处为my_mutex),显然互斥锁具有一个类型。

反对,你不能这样做:

void set(int new_value)
{
    guard lock(this_mutex_here);
    m_value = new_value;
}

int get() const
{
    SafeGuard lock(that_mutex_there);
    return m_value;
}

在第二个示例中,您使用了两个不同的互斥锁,但由于set()中的锁定不会阻止get()中的锁定而反之亦然,因此无法按预期工作。因此没有什么安全的(即使其中一名警卫被称为SafeGuard。)

因此规则是,如果您使用名为m_value的互斥锁保护my_mutex,则只要您访问m_value,就必须锁定my_mytex互斥锁。只要您以这种方式保持一致,您正在使用哪种实现无关紧要。