Помимо факта, что вам не нужно бросать из конструктора в вашем конкретном случае, потому что pthread_mutex_lock
фактически возвращает EINVAL , если ваш мьютекс не был инициализируется , и вы можете бросить после вызова lock
, как это сделано в std::mutex
:
void
lock()
{
int __e = __gthread_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
тогда в общем случае выброс из конструкторов в порядке для получения ошибок во время построения и в соответствии с RAII (Resource-acquisition-is-Initialization) программирования парадигма.
Проверьте это пример на RAII
void write_to_file (const std::string & message) {
// mutex to protect file access (shared across threads)
static std::mutex mutex;
// lock mutex before accessing file
std::lock_guard<std::mutex> lock(mutex);
// try to open file
std::ofstream file("example.txt");
if (!file.is_open())
throw std::runtime_error("unable to open file");
// write message to file
file << message << std::endl;
// file will be closed 1st when leaving scope (regardless of exception)
// mutex will be unlocked 2nd (from lock destructor) when leaving
// scope (regardless of exception)
}
Сосредоточьтесь на следующих утверждениях:
static std::mutex mutex
std::lock_guard<std::mutex> lock(mutex);
std::ofstream file("example.txt");
Первое утверждение - RAII и noexcept
. В (2) ясно, что RAII применяется к lock_guard
и на самом деле может throw
, тогда как в (3) ofstream
кажется не RAII, так как состояние объектов должно проверяться путем вызова is_open()
который проверяет флаг failbit
.
На первый взгляд кажется, что он не определился с тем, что он стандартным образом и в первом случае std::mutex
не выдает инициализацию *, в отличие от реализации OP *. Во втором случае он будет бросать все, что выброшено из std::mutex::lock
, а в третьем бросок вообще отсутствует.
Обратите внимание на различия:
(1) Может быть объявлено как статическое и фактически будет объявлено как переменная-член
(2) Фактически никогда не ожидается, что он будет объявлен как переменная-член
(3) Ожидается, что он будет объявлен как переменная-член, а базовый ресурс не всегда может быть доступен.
Все эти формы RAII ; Чтобы решить эту проблему, необходимо проанализировать RAII .
- Ресурс: ваш объект
- Приобретение (распределение): создаваемый вами объект
- Инициализация: ваш объект находится в инвариантном состоянии
Для этого не требуется инициализировать и подключать все в процессе строительства. Например, когда вы создаете объект сетевого клиента, вы фактически не подключаете его к серверу при создании, поскольку это медленная операция со сбоями. Вместо этого вы должны написать connect
функцию, чтобы сделать это. С другой стороны, вы можете создать буферы или просто установить их состояние.
Поэтому ваша проблема сводится к определению вашего начального состояния. Если в вашем случае ваше начальное состояние , мьютекс должен быть инициализирован , тогда вы должны выбросить из конструктора. В отличие от этого, просто прекрасно не инициализировать (как это делается в std::mutex
) и определить ваше инвариантное состояние как мьютекс создан . Во всяком случае, инвариант не обязательно скомпрометирован состоянием своего объекта-члена, поскольку объект mutex_
мутирует между locked
и unlocked
через Mutex
публичные методы Mutex::lock()
и Mutex::unlock()
.
class Mutex {
private:
int e;
pthread_mutex_t mutex_;
public:
Mutex(): e(0) {
e = pthread_mutex_init(&mutex_);
}
void lock() {
e = pthread_mutex_lock(&mutex_);
if( e == EINVAL )
{
throw MutexInitException();
}
else (e ) {
throw MutexLockException();
}
}
// ... the rest of your class
};