Подтвердить что ты не робот

Безопасно ли смешивать функции потоковой обработки потоков pthread.h и С++ 11?

Можно ли создать поток с помощью 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);
}

Кстати, я запускаю Debian Wheezy.

4b9b3361

Ответ 1

Вы можете на моей машине (Debian тоже). Но я не уверен, буду ли я называть это безопасным.

Если вы посмотрите на соответствующий файл, /usr/include/c++/4.7/i486-linux-gnu/bits/gthr-default.h в моем случае, вы увидите, что будет отображение 1:1 на pthreads api. <mutex> использует __gthread_mutex_lock для блокировки, которая точно определена там pthread_mutex_lock. Или вы увидите, что std::thread объявляет typedef __gthread_t native_handle_type;

Я не знаю, есть ли документированный способ проверить, используются ли pthreads. Но gthr-default.h определяет _GLIBCXX_GCC_GTHR_POSIX_H как включить guard, и я думаю, что до тех пор, пока этот макрос определен, вы можете предположить, что вы можете их смешивать.

Изменить: учитывая подсказку @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

В какой-либо спецификации нет никакой гарантии, что она будет работать, но, вероятно, любая реализация С++ в ОС, использующая pthreads в качестве единственной реальной библиотеки потоков, будет использовать pthreads под потоками С++, поэтому она, скорее всего, будет работать.

Вероятно, вы столкнетесь с проблемами, если позже попытаетесь перенести код на другую платформу, которая использует нечто, отличное от pthreads, даже если эта платформа поддерживает также pthreads (например, windows).

Возникают вопросы, зачем это беспокоить и рисковать? Если вы используете С++ 11 std::mutex, почему бы не использовать std::thread?

Ответ 3

Оба std::thread и std::mutex имеют метод native_handle, который позволяет вам копать до реализации платформы данного объекта. Это говорит мне, что стандартная библиотека потоковой обработки спроектирована так, чтобы хорошо сочетаться с реализацией платформы.

В качестве сторонних std::thread и std::mutex используются разные объекты, которые делают разные вещи, а именно: управлять потоками и обеспечивать синхронизацию поперечных потоков. В конце ядро ​​делает тяжелый подъем.

Итак, если вас не беспокоит переносимость, я не понимаю, почему это должно быть проблемой.

В стороне, иногда вам может понадобиться реализация собственной платформы, чтобы предоставить вам более богатый набор функций, который позволяет платформа. Например, потоковая передача BSD позволяет использовать различные типы потоков, а некоторые библиотеки потоков позволяют вам устанавливать размер стека для вашего нового потока. API-интерфейсы потоков С++ представляют собой переносимый самый низкий общий знаменатель.

Ответ 4

Если ваш вопрос: могу ли я свободно переключаться между одним типом мьютекса и другим наугад? Тогда ответ будет отрицательным (если все нижние слои не используют одну и ту же реализацию, например pthread_mutex.)

Однако, если у вас есть разные группы ресурсов, которые вы хотите защитить, любая одна группа ресурсов может быть защищена с помощью одной реализации. На самом деле, иногда может быть лучше использовать семафор (то есть семафор полезен для определения одного замка для записи, много блокировок для чтения).

Итак, если у вас есть 4 группы ресурсов, которыми вы управляете, вы можете использовать:

  • станд:: мьютекс,
  • pthread_mutex,
  • подталкивание:: мьютекс,
  • семафоры.

То, что вы не можете сделать, это использовать boost:: mutex для доступа к данным, защищенным семафорами и наоборот, или std:: mutex для использования вещей, защищенных pthread_mutex.

В качестве простого примера, с точки зрения кода, это означает, что геттер и сеттер будут выполняться следующим образом:

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. Какая реализация, которую вы используете, не имеет значения, если вы согласны в этом.