сигнал и порядок разблокировки - PullRequest
15 голосов
/ 21 июня 2011
void WorkHandler::addWork(Work* w){
    printf("WorkHandler::insertWork Thread, insertWork locking \n");
    lock();
    printf("WorkHandler::insertWork Locked, and inserting into queue \n");
    m_workQueue.push(w);
    signal();
    unLock();
}

Я следовал за учебником, и я получил это. Мне было интересно, если это нормально, чтобы изменить порядок singal () и unLock (), как это

void WorkHandler::addWork(Work* w){
    printf("WorkHandler::insertWork Thread, insertWork locking \n");
    lock();
    printf("WorkHandler::insertWork Locked, and inserting into queue \n");
    m_workQueue.push(w);
    unLock();
    signal();
}

Если я не могу сделать это, не могли бы вы рассказать мне, почему мне не разрешено это делать? Заранее спасибо.

Ответы [ 3 ]

22 голосов
/ 21 июня 2011

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

pthread_mutex_lock(mutex);
while (!predicate)
  pthread_cond_wait(cvar);
pthread_mutex_unlock(mutex);

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

Однако возможны две проблемы с производительностью.

  • «Спешите и подождите».По сути, если вы подаете сигнал, пока блокировка удерживается, другому потоку все еще нужно ждать, пока мьютекс не станет доступен.Многие реализации pthreads вместо пробуждения другого потока просто перемещают его в очередь ожидания мьютекса, сохраняя ненужный цикл wakeup-> wait.В некоторых случаях, однако, это не реализовано или недоступно, что приводит к потенциальному ложному переключению контекста или IPI.
  • Ложные пробуждения.Если вы дадите сигнал после разблокировки, другой поток сможет выполнить другое пробуждение.Рассмотрим следующий сценарий:

    1. Поток A начинает ожидать добавления элементов в потокобезопасную очередь.
    2. Поток B вставляет элемент в очередь.После разблокировки очереди, но до того, как он выдает сигнал, происходит переключение контекста.
    3. Поток C вставляет элемент в очередь и выдает сигнал cvar.
    4. Поток A пробуждается иобрабатывает оба элемента.Затем он возвращается к ожиданию в очереди.
    5. Поток B возобновляет работу и сигнализирует cvar.
    6. Поток A просыпается, а затем немедленно возвращается в спящий режим, поскольку очередь пуста.

    Как вы можете видеть, это может привести к ложному пробуждению, которое может потратить некоторое время процессора.

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

2 голосов
/ 21 июня 2011

Ответ на ваш вопрос «Да».На самом деле, это немного предпочтительнее (как вы, наверное, догадались), так как избегает проблемы «поторопиться и ждать» при пробуждении потока для проверки условия только для того, чтобы он немедленно блокировался на мьютексе, который он должен получить перед тестированиемусловие.

Этот ответ основан на предположении, что эти вещи верны:

  • lock - это тонкая оболочка для pthread_mutex_lock.
  • unLock является тонкой оболочкой для pthread_mutex_unlock.
  • signal является вещью-оберткой для pthread_cond_signal.
  • Мьютекс, который вы блокируете и разблокируете, тот, который вы даете pthread_cond_wait.
1 голос
/ 15 апреля 2017

Эта статья действительно стоит прочитать на ваш вопрос:

Сигнал с мьютексом или нет?

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

  1. ждать сигнала (условная переменная), удерживая мьютекс. В результате поток может присоединиться к очереди условного var и перейти в спящий режим.
  2. сигнализируется, но без мьютекса. В этом случае поток не будет спать, но заблокировать его. (Ошибка, которую я сделал в этом, заключается в том, что я думал, что она тоже будет спать. В этом случае, если сигналы производителя и переключение контекста происходят прямо перед тем, как он освобождает мьютекс, тогда все потоки проснутся и узнают, что не могут заблокировать мьютекс, иди спать вечно. Это неправильно, потому что они не будут спать, а будут ждать и блокировать).

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

Сигнал перед разблокировкой мьютекса может привести к ложному пробуждению. Если ваш код плохо спроектирован для обработки предикатных изменений, сделанных в результате ложного пробуждения, вы должны выбрать сигнал, удерживая блокировку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...