пусть существует только один поток в котором выполняется эта функция.
Все работает прекрасно, т.е. mutex в рамках одного потока позволяет такую вот "рекурсию".
В данном примере можно пользоваться легковесным рекурсивным мутексом, известным как "критическая секция".
А вообще, стоит задуматься — нужна ли рекурсия.
After a thread obtains ownership of a mutex, it can specify the same mutex in repeated calls to the wait-functions without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns. To release its ownership under such circumstances, the thread must call ReleaseMutex once for each time that the mutex satisfied the conditions of a wait function.
Здравствуйте, rm822, Вы писали:
R>Это будет семафор
Нет, у семафора иная логика.
Во-первых, его нельзя неограниченно рекурсивно захватывать одним потоком: как только счётчик дойдёт до нуля, поток повиснет в ожидании.
Во-вторых, если исходное значение счётчика равно 2, то оба потока смогут по одному разу его схватить, а затем повиснуть во взаимном ожидании.
То есть, ко всему прочему, это ещё и возможность для дедлоков.
Я бы сперва определился, какую роль играет этот мутекс — обслуживает критическую секцию, барьер, условную переменную...
И если это не критическая секция, то крепко задумался бы о пользе мониторов и условных переменных.
К сожалению, в WinAPI этих инструментов как примитивов нет; но бог милостив, ещё Дейкстра доказал, что любой механизм синхронизации может быть выражен в базисе семафоров.
Поэтому берём ACE, boost/thread, обероно-образную библиотеку (эх, сейчас ссылку не могу найти)...
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, rm822, Вы писали:
R>>Это будет семафор
К>Нет, у семафора иная логика. К>Во-первых, его нельзя неограниченно рекурсивно захватывать одним потоком: как только счётчик дойдёт до нуля, поток повиснет в ожидании. К>Во-вторых, если исходное значение счётчика равно 2, то оба потока смогут по одному разу его схватить, а затем повиснуть во взаимном ожидании. К>То есть, ко всему прочему, это ещё и возможность для дедлоков.
К>Я бы сперва определился, какую роль играет этот мутекс — обслуживает критическую секцию, барьер, условную переменную... К>И если это не критическая секция, то крепко задумался бы о пользе мониторов и условных переменных. К>К сожалению, в WinAPI этих инструментов как примитивов нет; но бог милостив, ещё Дейкстра доказал, что любой механизм синхронизации может быть выражен в базисе семафоров. К>Поэтому берём ACE, boost/thread, обероно-образную библиотеку (эх, сейчас ссылку не могу найти)...
Собственно он и хотел чтобы второй Wait заблокировался, DEN>Можно ли что-то сделать, чтобы второй вызов DEN>
T>After a thread obtains ownership of a mutex, it can specify the same mutex in repeated calls to the wait-functions without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns. To release its ownership under such circumstances, the thread must call ReleaseMutex once for each time that the mutex satisfied the conditions of a wait function.
Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN?
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, DENIVA, Вы писали:
К>В данном примере можно пользоваться легковесным рекурсивным мутексом, известным как "критическая секция". К>А вообще, стоит задуматься — нужна ли рекурсия.
Здравствуйте, DENIVA, Вы писали:
DEN>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии?
Можно. Нужно только немного изменить твой код:
В приведённом тобой коде содержится ошибка, или не хватает информации о том, чего ты хочешь добиться.
Ведь если бы mutex не был рекурсивный, то он бы повис в ожидании освобождения на втором вызове WaitForSingleObject, а так как освободить его должен этот же поток, то вот он dedlock!
Опиши задачу более подробно — тогда будет проще тебе помочь.
Здравствуйте, Tonal-, Вы писали:
T>Здравствуйте, DENIVA, Вы писали:
DEN>>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? T>Можно. Нужно только немного изменить твой код: T>
T>
T>В приведённом тобой коде содержится ошибка, или не хватает информации о том, чего ты хочешь добиться. T>Ведь если бы mutex не был рекурсивный, то он бы повис в ожидании освобождения на втором вызове WaitForSingleObject, а так как освободить его должен этот же поток, то вот он dedlock! T>Опиши задачу более подробно — тогда будет проще тебе помочь.
Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.
Здравствуйте, DENIVA, Вы писали:
DEN>Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.
Ты можешь объяснить внятно, кто будет освобождать мьютекс, занятый первым вызовом, если тред, владеющий им, висит в ожидании на втором вызове?!
Здравствуйте, Draqon, Вы писали:
D>Здравствуйте, DENIVA, Вы писали:
DEN>>Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.
D>Ты можешь объяснить внятно, кто будет освобождать мьютекс, занятый первым вызовом, если тред, владеющий им, висит в ожидании на втором вызове?!
код которы я прислал в самом первом письме работает отлично. никаких deadlock там нет
Здравствуйте, Draqon, Вы писали:
D>Здравствуйте, DENIVA, Вы писали:
DEN>>Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.
D>Ты можешь объяснить внятно, кто будет освобождать мьютекс, занятый первым вызовом, если тред, владеющий им, висит в ожидании на втором вызове?!
Это проблема приложений, а не библиотеки. Главное, чтобы у пользователя было две возможности, а как он будет это реализовывать — его дело
Здравствуйте, DENIVA, Вы писали:
DEN>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN?
Я так понял, что ты хочешь, чтобы другой поток вместо текущего освобождал мьютекс, а твой ждал?
Чем тебя тогда события с автосбросом не устраивают? Только не надо говорить "ничего менять не будем. надо с помощью мьютекса"
Уважайте собеседника, даже если не согласны с его мнением
Здравствуйте, AgentRX, Вы писали:
ARX>Здравствуйте, DENIVA, Вы писали:
DEN>>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN? ARX>Я так понял, что ты хочешь, чтобы другой поток вместо текущего освобождал мьютекс, а твой ждал?
Правильно понял ARX>Чем тебя тогда события с автосбросом не устраивают? Только не надо говорить "ничего менять не будем. надо с помощью мьютекса"
Можно и без Mutex Спасибо за совет. Пойду почитаю про "события с автосбросом"
Здравствуйте, DENIVA, Вы писали:
К>>А вообще, стоит задуматься — нужна ли рекурсия. DEN>Думать нечего. Это требование спецификации
Нечасто встречаются такие спецификации, в которых оговорено: "здесь использовать рекурсивный мутекс".
По моему опыту, рекурсивная блокировка возможна только в очень простых случаях, когда взаимодействие потоков очевидно, и можно доказать, что дедлоков нет. Цель рекурсивной блокировки — ослабить предусловия (касающиеся статуса мутекса) у функций, вызываемых в рамках потока
void threadfunc() // предусловие: уровень блокировки (нашим потоком) = 0
{
.....
subroutine1(); // уровень = 0
.....
subroutine2(); // уровень = 0
.....
}
void subroutine1() // предусловия нет
{
.....
lock(); ..... unlock();
.....
lock();
.....
subroutine2(); // уровень = 1
.....
unlock();
.....
}
void subroutine2() // предусловия нет
{
.....
lock(); ..... unlock(); // поэтому мы подстраховываемся
.....
}
То есть, вместо того, чтобы разделить весь код на "небезопасные" и "безопасные" зоны, мы вставляем блокировки ровно там, где они нам потребовались.
Вот пример, когда из-за этого возникает дедлок:
HEVENT shutdown;
HMUTEX guard;
int data;
// поток 1void writer()
{
while(WaitForSingleObject(shutdown,0)!=WAIT_OBJECT_0)
{
WaitForSingleObject(guard,INFINITE);
data = rand();
ReleaseMutex(guard);
}
}
// поток 2void reader()
{
while(WaitForSingleObject(shutdown,0)!=WAIT_OBJECT_0)
{
WaitForSingleObject(guard,INFINITE);
printf("%d ... ", data);
subroutine();
printf("%d\n", data);
ReleaseMutex(guard);
}
}
void subroutine() // предусловие: мутекс не заблокирован (но мы упустили это из виду)
{
int olddata;
WaitForSingleObject(guard,INFINITE); olddata = data; ReleaseMutex(guard);
// здесь мы наивно предположили, что блокировка снятаint count = 0;
int newdata = olddata;
while(newdata == olddata)
{
Sleep(1000);
++count;
// и поэтому надеемся, что рано или поздно writer() запишет туда что-нибудь новое
WaitForSingleObject(guard,INFINITE); newdata = data; ReleaseMutex(guard);
}
printf("we have waited %d seconds for new data.", count);
}
Естественно, в реальной жизни примеры не столь дистиллированные. Но именно поэтому требование рекурсивной блокировки — это ЗВОНОЧЕК.
Здравствуйте, AgentRX, Вы писали:
DEN>>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN?
ARX>Я так понял, что ты хочешь, чтобы другой поток вместо текущего освобождал мьютекс, а твой ждал? ARX>Чем тебя тогда события с автосбросом не устраивают? Только не надо говорить "ничего менять не будем. надо с помощью мьютекса"
События с автосбросом... или же семафоры.
А всё-таки, я бы попробовал не маяться с жонглированием низкоуровневыми средствами синхронизации, а выразить взаимодействие через высокий уровень. Ну хотя бы через условные переменные.