我可以使用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。
答案 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::thread
和std::mutex
都有native_handle
方法,可让您深入了解给定对象的平台实现。这告诉我标准的线程库旨在与平台实现相得益彰。
旁边std::thread
和std::mutex
是不同的对象,可以做不同的事情。管理线程并提供跨线程同步。最后,内核完成了繁重的任务。
因此,如果您不担心可移植性,我不明白为什么这应该是一个问题。
顺便说一句,有时您可能需要本机平台实现,以便为您提供平台允许的更丰富的功能集。例如,BSD线程允许不同类型的线程,一些线程库允许您设置新线程的堆栈大小。 C ++线程API是可移植的最低公分母。
答案 3 :(得分:0)
如果你的问题是:我可以随意使用一种互斥体和另一种互斥体之间的自由切换吗?然后答案是否定的(除非所有底层都使用相同的实现,例如pthread_mutex。)
但是,如果您要保护不同的资源组,则可以使用任何一个实现保护任何一组资源。实际上,使用信号量有时会更好(例如,信号量对于定义一个写入锁定很有用,许多锁定用于读取)。
因此,如果您有4组资源,您可以使用:
你不能做的是使用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
互斥锁。只要您以这种方式保持一致,您正在使用哪种实现无关紧要。