Я всегда находил синхронизированные операторы Java чистым способом создания взаимных блокировок, таких как блокировка и разблокировка:
public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}
Хотя фундаментальная концепция мониторов, используемых в мьютексах Java и pthread, различна, мьютексы pthread в их самом основном часто используются как:
void addName(char* name) {
int status = -1;
status = pthread_mutex_lock(this->lock);
if (status == 0) {
this->lastName = name;
this->nameCount++;
pthread_mutex_unlock(this->lock);
}
nameList->add(name);
}
Я понимаю, что приведенный выше код не использует возможности мьютексов pthread. Он также не обрабатывает все сценарии ошибок. Тем не менее, это, вероятно, самый распространенный способ использования мьютексов pthread. Сказав это, я думаю, что было бы неплохо иметь более чистую идиому для таких синхронизаций:
public void addName(char* name) {
synchronized(this->lock) {
this->lastName = name;
this->nameCount++;
}
nameList.add(name);
}
Так возможно ли это сделать на C, C ++?
Классический подход — это объект блокировки. Маленький объект, конструктор которого получает мьютекс, а деструктор освобождает его. Создание объекта блокировки внутри области действия имеет тот же эффект, что и синхронизированная область в Java.
C ++ 11 уже имеет все, что нужно для этого, в виде std::mutex
или std::recursive_mutex
а также std::unique_lock
,
Следующий подход приведет к тому же результату, что и синхронизированный метод в Java:
1) Объявить std::recursive_mutex
ученик
2) Получить мьютекс в методе.
Пример:
class whatever {
private:
std::recursive_mutex s_mutex;
// ... The rest of the class definition.
public:
// ...
void addName();
// ...
};
void whatever::addName()
{
std::unique_lock<std::recursive_mutex> s_lock(s_mutex);
// ... the rest of the method
}
Это не должно быть сделано во всей области действия метода:
void whatever::addName(char* name)
{
{
std::unique_lock<std::recursive_mutex> s_lock(s_mutex);
this->lastName = name;
this->nameCount++;
}
nameList.add(name);
}
Подвести итоги:
А) Обновление до C ++ 11, которое предлагает C ++ версии мьютексов и блокировок POSIX.
Б) Если вы не можете обновить систему до C ++ 11, напишите свой собственный мьютекс и методы блокировки. Вы хотите избежать явного pthread_mutex_lock
и разблокировать каждый раз, самостоятельно. Легко забыть разблокировать мьютекс, явно, на каком-то пути выхода, и в итоге получится беспорядок. C ++ сделает это за вас, если вы правильно заключите их в методы класса.
У нас может быть что-то вроде:
#define SYNC_R rwlock_rd
#define SYNC_W rwlock_wr
#define SYNC_S spin_
#define SYNC_M mutex_
#define pthread_rwlock_wrunlock pthread_rwlock_unlock
#define pthread_rwlock_rdunlock pthread_rwlock_unlock
#define __SYNC_X__(lockObj, lockType) for(int __sync_status__=!pthread_##lockType##lock(lockObj); __sync_status__ ; pthread_##lockType##unlock(lockObj), __sync_status__=0)
#define __SYNC_M__(lockObj) for(int __sync_status__=!pthread_mutex_lock(lockObj); __sync_status__ ; pthread_mutex_unlock(lockObj), __sync_status__=0)
#define __SYNCALIAS__(_1, _2, NAME, ...) NAME
#define __SYNC__(...) __SYNCALIAS__(__VA_ARGS__, __SYNC_X__, __SYNC_M__)(__VA_ARGS__)
#define synchronized(...) __SYNC__(__VA_ARGS__)
Это позволяет нам иметь два варианта блоков синхронизации. Первый — только для мьютексов:
synchronized(this->lock)
// ==> __SYNC__(this->lock)
// ==> __SYNCALIAS__(this->lock, __SYNC_X__, __SYNC_M__)(this->lock)
// ==> __SYNC_M__(this->lock)
// ==> for(int __sync_status__=!pthread_mutex_lock(lockObj); __sync_status__ ; pthread_mutex_unlock(lockObj))
{
this->lastName = name;
this->nameCount++;
}
В качестве альтернативы мы можем использовать rwlocks, spinlocks или даже мьютексы с дополнительным параметром:
—
synchronized(this->rwlock, SYNC_W)
// ==> __SYNC__(this->rwlock, rwlock_wr)
// ==> __SYNCALIAS__(this->rwlock, rwlock_wr, __SYNC_X__, __SYNC_M__)(mm, rwlock_rd)
// ==> __SYNC_X__(this->rwlock, rwlock_wr)
// ==> for(int __sync_status__=!pthread_rwlock_wrlock(lockObj); __sync_status__ ; pthread_rwlock_wrunlock(lockObj))
// ==> for(int __sync_status__=!pthread_rwlock_wrlock(lockObj); __sync_status__ ; pthread_rwlock_unlock(lockObj))
{
this->lastName = name;
this->nameCount++;
}
Проблема в том, что он не обрабатывает сценарии ошибок чисто. Так что, возможно, есть лучший способ сделать это. Хотя этот вариант подходит как для C, так и для C ++, у C ++, вероятно, будут свои собственные решения. Расширения GCC были еще одним проспектом, который я не исследовал.
я нахожу std::lock_guard()
может использоваться аналогично синхронизированной функции Java:
class MyType
{
std::mutex mtx;
public:
void synced_functionA()
{
std::lock_guard<std::mutex> lock(mtx);
// synchronized code
}
void synced_functionB()
{
std::lock_guard<std::mutex> lock(mtx);
// synchronized code
}
};
В C ++ RAII дает что-то почти такое же аккуратное, как synchronized
блок, и начиная с C ++ 11 есть стандартная библиотека потоков, использующая это:
{
std::lock_guard<std::mutex> lock(mutex);
lastName = name;
nameCount++;
} // lock released here
nameList.add(name);
В C вам придется вручную получить и снять блокировку, как вы описываете; это гораздо более простой язык, который не поддерживает автоматическое управление ресурсами.