pthread_mutex_lock как не блокировать, когда это тот же поток

Я использую pthread_mutex_t для блокировки.

pthread_mutex_t m_lock;

void get1() {
cout<<"Start get 1"<<endl;
pthread_mutex_lock(&m_lock);
get2();
pthread_mutex_unlock(&m_lock);
cout<<"End get 1"<<endl;
}

void get2() {
cout<<"Start get 2"<<endl;
pthread_mutex_lock(&m_lock); // The program actually stops here because it waits to m_lock to be unlock from get1 function.
pthread_mutex_unlock(&m_lock);
cout<<"End get 2"<<endl;
}

// The thread call to run function
void* run(void* p) {
get1();
}

Допустим, у меня есть только один поток, который вызывает функцию, поэтому:
get1 блокирует m_lock и вызывает get2, но когда он пытается заблокировать m_lock, он ожидает, что блокировка будет разблокирована (чего не происходит), и мы получили тупик.

Мой вопрос: как я могу избежать этого случая, когда той же нити, которая заблокировала блокировку в get1, не нужно будет ждать блокировки в get2 (потому что это та же нить)?

Например, в Java этот случай никогда не может произойти, когда вы используете synchornized.

public Test implements Runnable {
public void get1() {
System.out.println("Start get 1");
synchronized (this) {
get2();
}
System.out.println("End get 1");
}

public void get2() {
System.out.println("Start get 2");
synchronized (this) {

}
System.out.println("End get 2");
}

@Override
public void run() {
get1();
}
}

Здесь нет тупика.

Я хочу, чтобы тот же результат в моем коде C, пожалуйста.

Благодарю.

8

Решение

Как отмечает Ками Казе в комментариях, если это ваш полный пример, то это не проблема: есть только один путь, ведущий к get2и этот путь уже приобретает мьютекс; просто не покупайте его во второй раз.

Однако в целом можно подумать о сценариях, где это не так ясно. В этом случае вы можете сделать мьютекс рекурсивный / возвратный:

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

В ваших настройках это будет через pthread_mutexattr_settype:

pthread_mutexattr_settype(&m_lock, PTHREAD_MUTEX_RECURSIVE);
11

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

С этим:

pthread_mutex_lock(&m_lock);
get2();
pthread_mutex_unlock(&m_lock);

вы заперли весь get2(), Так что нет смысла брать так же заблокировать снова внутри get2() функция.
Просто удалите код блокировки и разблокировки из get2(),

Если только часть кода в get2() требует блокировки, а затем избавиться от блокировки и разблокировки от get1() функция.

Например, в Java этот случай никогда не может произойти, когда вы используете
synchornized.

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

0

Это называется рекурсией блокировки.

Последний аргумент pthread_mutex_init является структурой атрибутов. Вы можете установить атрибуты, чтобы разрешить рекурсивную блокировку с pthread_mutexattr_settype(..., PTHREAD_MUTEX_RECURSIVE),

Но я должен добавить некоторые редакционные материалы здесь. Я очень сильно верю, что рекурсия блокировки — это почти всегда ошибка. Или это приведет к невозможности отладки ошибок позже в течение времени жизни программ.

Операция блокировки может объясняться как «когда функция блокировки возвращает объект, защищенный блокировкой, в известном состоянии, и это состояние не изменится, пока не будет вызвана функция разблокировки». Это означает, что если get1 начал изменять объект, который вы защищаете с помощью блокировки, а затем get2 повторяет этот замок, этот контракт нарушается дважды. Во-первых, потому что get2 успешно получает блокировку, пока объект не находится в известном состоянии, во-вторых, потому что объект изменен в то время как get1 думает, что он владеет замком.

Конечно, мы часто совершаем подобные вещи, но это ужасная практика. Перепроектируйте свою программу, чтобы не повторять блокировки. Стандартный способ сделать это — реализовать функцию get2_locked а также get2 получает блокировку и звонки get2_locked в то время как get1 уже знает, что у него есть замок и будет звонить get2_locked,

0

Я предполагаю что get1 на самом деле делает больше, чем просто захватывает замок get2? Иначе какой смысл get1?

Если это так, вы можете решить это, имея get3 функция, которая делает основную часть get2 (часть, которую вы здесь не показываете) и которая не блокируется. Затем вызовите эту новую функцию из get1 вместо этого (и, конечно, из get тоже):

void get1()
{
// Do something here

cout<<"Start get 1"<<endl;
pthread_mutex_lock(&m_lock);
get3();  // <-- Note call get3 instead here
pthread_mutex_unlock(&m_lock);
cout<<"End get 1"<<endl;

// Do something more here
}

void get2()
{
cout<<"Start get 2"<<endl;
pthread_mutex_lock(&m_lock); // The program actually stops here because it waits to m_lock to be unlock from get1 function.
get3();  // <-- Note call to get3 here
pthread_mutex_unlock(&m_lock);
cout<<"End get 2"<<endl;
}

void get3()
{
// Do the actual work of get2 here...
// Note: No locking here
}
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector