Возможно ли иметь Java как операторы синхронизации в C, C ++?

Я всегда находил синхронизированные операторы 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 ++?

1

Решение

Классический подход — это объект блокировки. Маленький объект, конструктор которого получает мьютекс, а деструктор освобождает его. Создание объекта блокировки внутри области действия имеет тот же эффект, что и синхронизированная область в 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 ++ сделает это за вас, если вы правильно заключите их в методы класса.

1

Другие решения

У нас может быть что-то вроде:

#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 были еще одним проспектом, который я не исследовал.

Также сделал ссылку на реализацию в sync.h, sync.c.

0

я нахожу 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
}
};
0

В C ++ RAII дает что-то почти такое же аккуратное, как synchronized блок, и начиная с C ++ 11 есть стандартная библиотека потоков, использующая это:

{
std::lock_guard<std::mutex> lock(mutex);
lastName = name;
nameCount++;
} // lock released here

nameList.add(name);

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

0
По вопросам рекламы [email protected]