Существует ли шаблон проектирования C ++, который реализует механизм или мьютекс, который контролирует количество времени, которое поток может владеть заблокированным ресурсом? - PullRequest
0 голосов
/ 08 февраля 2019

Я ищу способ гарантировать, что каждый раз, когда поток блокирует определенный ресурс, он вынужден освобождать этот ресурс через определенный период времени (если он еще не освободил его).Представьте себе соединение, в котором вам нужно ограничить время, в течение которого конкретный поток может владеть этим соединением.

Я предполагаю, что его можно использовать следующим образом:

{
    std::lock_guard<std::TimeLimitedMutex> lock(this->myTimeLimitedMutex, timeout);
    try {
        // perform some operation with the resource that myTimeLimitedMutex guards. 
    }
    catch (MutexTimeoutException ex) {
        // perform cleanup
    }
}

Я вижу, что тамявляется timed_mutex, который позволяет программе тайм-аут, если блокировка не может быть получена.Мне нужно, чтобы тайм-аут наступил после блокировки.

Уже есть некоторые ситуации, когда вы получаете ресурс, который может быть неожиданно забран.Например, сокеты tcp - после того, как установлено соединение с сокетом, код на каждой стороне должен обрабатывать случай, когда другая сторона разрывает соединение.

Я ищу шаблон, который обрабатывает типы ресурсов, которыеобычно тайм-аут по своему усмотрению, но когда они этого не делают, они должны быть сброшены.Это не должно обрабатывать каждый тип ресурса.

Ответы [ 5 ]

0 голосов
/ 09 февраля 2019

Переменные «Condition» могут иметь тайм-ауты.Это позволяет вам ждать, пока поток добровольно не освободит ресурс (с помощью notify_one () или notify_all ()), но само ожидание истечет по истечении указанного фиксированного промежутка времени.

Примеры в документации Boost для «условий» могут прояснить это.

Если вы хотите форсировать релиз, вы должны написать код, который форсирует его.Это может быть опасно.Код, написанный на C ++, может делать довольно близкие вещи.Ресурс может иметь доступ к реальному оборудованию, и он может ждать, пока он что-то завершит.Может быть физически невозможно закончить то, на чем застряла программа.

Однако, если это возможно, вы можете обработать его в потоке, в котором время ожидания () истекло.

0 голосов
/ 09 февраля 2019

Вы не можете сделать это только с C ++.

Если вы используете систему Posix, это можно сделать.Вам нужно будет активировать сигнал SIGALARM, который не будет маскирован только для потока, время которого истекло.В обработчике сигнала вам нужно установить флаг и использовать longjmp для возврата к коду потока.В коде нити в позиции setjmp вы можете вызываться только в том случае, если сигнал сработал, поэтому вы можете выдать исключение Timeout.

Пожалуйста, см. этот ответ длякак это сделать.

Кроме того, в linux кажется , который вы можете напрямую выбросить из обработчика сигнала (поэтому здесь нет longjmp / setjmp).

Кстати, еслиЯ был тобой, я бы закодировал обратное.Подумайте об этом: вы хотите сказать ветке: «Эй, ты слишком долго, так что давайте отбросим всю (долгую) работу, которую ты проделал, чтобы я мог добиться прогресса».В идеале, вы должны сделать так, чтобы ваш длинный поток был более кооперативным, делая что-то вроде: «Я сделал A задачи ABCD, давайте освободим мьютекс, чтобы другие могли прогрессировать на A. Затем давайте проверим, могу ли я снова сделать это для выполнения B искоро."Вы, вероятно, хотите быть более мелкозернистыми (иметь больше мьютекса на меньших объектах, но убедитесь, что вы блокируете в том же порядке) или использовать блокировки RW (чтобы другие потоки могли использовать объекты, если вы их не модифицируете),и т.д ...

0 голосов
/ 08 февраля 2019

Поддерживаю SergeyAs ответ.Освобождение заблокированного мьютекса после истечения времени ожидания является плохой идеей и не может работать. Mutex означает взаимное исключение, и это сложный контракт, который нельзя нарушать.

Но вы можете делать то, что вы хотите:

Проблема: Вы хотите гарантировать, что ваши потоки не удерживают мьютекс дольше определенного времени T.

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

Для этого (примеры):

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

Существуют исключения из этих правил, но общее правило:

  • Сделайте ваш код немного менее оптимальным (например, сделайте избыточное копирование внутри критической секции), чтобы критическая секция сталаКороче как можно.Это хорошее многопоточное программирование.

(*) Это всего лишь примеры операций, когда возникает соблазн заблокировать весь список, выполнить операции, а затем разблокировать список.Вместо этого рекомендуется просто взять локальную копию списка и очистить исходный список, пока мьютекс заблокирован, в идеале с помощью операции swap(), предлагаемой большинством контейнеров STL.А затем выполните медленную операцию с локальной копией вне критического раздела.Это не всегда возможно, но всегда стоит учитывать.В худшем случае сортировка имеет квадратную сложность и обычно требует произвольного доступа ко всему списку.Полезно отсортировать (копию) списка за пределами критического раздела, а затем проверить, нужно ли добавлять или удалять элементы.Распределение памяти также имеет некоторые сложности, поэтому следует избегать массового выделения / освобождения памяти.

0 голосов
/ 09 февраля 2019

Такой подход не может быть реализован, потому что владельцу мьютекса нужна возможность очистить все, что осталось в недопустимом состоянии в процессе транзакции.Это может занять неизвестное произвольное количество времени.

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

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

0 голосов
/ 08 февраля 2019

Это не может работать, и это никогда не будет работать.Другими словами, это никогда не может быть сделано.Это идет вразрез со всей концепцией собственности и атомных транзакций.Потому что, когда поток получает блокировку и реализует две транзакции подряд, он ожидает, что они станут атомно видимыми для внешнего слова.В этом случае вполне возможно, что транзакция будет разорвана - первая ее часть будет выполнена, а вторая - нет.

Что еще хуже, так как блокировка будет принудительно удалена, частично выполненная транзакция станет видимой для внешнего слова, прежде чем прерванный поток сможет откатиться.

Эта идеяидет вразрез со всей школой многопоточного мышления.

...