Is it safe to mix pthread.h and C++11 standard library threading features?

匆匆过客 提交于 2019-12-30 05:36:16

问题


Can I spawn a thread with pthread_create and use std::mutex inside of it safely?

I would think that if std::mutex is implemented as a pthread_mutex_t then it would be fine but I don't see this documented anywhere

For example:

#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 I'm running Debian Wheezy.


回答1:


You could on my machine (Debian too). But I'm not sure if I would call this safe.

If you look at the relevant file, /usr/include/c++/4.7/i486-linux-gnu/bits/gthr-default.h in my case, you will see that there will be a 1:1 mapping to the pthreads api. <mutex> uses __gthread_mutex_lock for locking which is defined exactly there to pthread_mutex_lock. Or you will see that std::thread declares typedef __gthread_t native_handle_type;

I don't know if there is a documented way to check if pthreads are used. But gthr-default.h defines _GLIBCXX_GCC_GTHR_POSIX_H as include guard and I think as long as this macro is defined, you can assume that you can mix them both.

Edit: Given the hint from @Wakely, I would write:

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:


There's no guarentee in any spec that it will work, but it's likely that any C++ implementation on an OS that uses pthreads as its only real threading library will use pthreads underneath C++ threads, so it will likely work.

You will likely run into problems if you later try to port the code to some other platform that uses something other than pthreads, even if that platform supports pthreads too (eg, windows).

The questions is, why bother and risk it? If you're using C++11 std::mutex, why not use std::thread as well?




回答3:


Both std::thread and std::mutex have a native_handle method which allows you to dig down to the platform implementation of the given object. This says to me that the standard threading library is designed to play nice with the platform implementation.

As an aside std::thread and std::mutex are different objects that do different things viz. manage threads and provide cross thread synchronization. In the end the kernel does the heavy lifting.

So if you are not worried about portability, I cannot see why this should be an issue.

As an aside, sometimes you may need the native platform implementation so as to provide you with the richer feature-set that the platform allows. For example BSD threading allows different types of threads and some threading libraries allow you to set the stack size of your new thread. The C++ threading APIs are a portable lowest common denominator.




回答4:


If your question is: can I use freely switch between one type of mutex and another at random? Then the answer is no (unless all the bottom layers use the same implementation, such as pthread_mutex.)

However, if you have different groups of resources that you want to protect, any one group of resources can be protected with any one implementation. Actually, it can at times be better to use a semaphore (i.e. semaphore are useful to define a one lock for write, many locks for reads).

So, if you have 4 groups of resources you are managing, you may use:

  • std::mutex,
  • pthread_mutex,
  • boost::mutex,
  • semaphores.

What you cannot do is use the boost::mutex to access data protected by the semaphores and vice versa, or std::mutex to use things protected by pthread_mutex.

As a simple example, in terms of code, this means a getter and a setter would be done this way:

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

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

The two functions use the same mutex (here my_mutex) and obvious the mutex has one type.

Opposed to that, you could not do that:

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;
}

In this second example, you use two different mutexes and that won't work as expected because the lock in set() won't block the lock in get() and vice versa. So there is nothing safe about it (even if one of the guards is called SafeGuard.)

So the rule is, if you protect m_value with a mutex named my_mutex, any time you access m_value you must lock the my_mytex mutex. Which implementation you are using does not matter, as long as you are consistent in this way.



来源:https://stackoverflow.com/questions/15367988/is-it-safe-to-mix-pthread-h-and-c11-standard-library-threading-features

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!