почему мне не нужно переносить операторы блокировки / разблокировки при использовании условий ожидания qt?

0

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

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

Во-вторых, я немного смущен тем, что условия ожидания делают в своих примерах. Похоже, что они блокируют мьютексы, проверяют условие ожидания и блокируют, продолжаются, когда выполняется условие, а затем отпирают мьютекс прямо там. Разве это не означает, что доступ к буфере для записи полностью незащищен в этот момент?

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

Теги:
multithreading
qt

2 ответа

1
Лучший ответ

доступ к буферам защищен в силу того, как выбран индекс данных:

  1. потребитель не видит данных в буфере, поэтому блоки
  2. производитель видит, что буфер не заполнен, поэтому записывает некоторые данные в буфер с индексом 0
    и пробуждает потребителя и увеличивает индекс t на индекс 1
  3. потребитель видит данные и считывает их по индексу 0

в частности, единственный способ i % buffersize от потребителя будет таким же, как i % buffersize от производителя между критическими разделами, это если numUsedBytes == 0 и вы являетесь производителем или numUsedBytes == buffersize и вы являетесь потребителем

видя это, вы должны быть в состоянии рассуждать о том, что ни производитель, ни потребитель не смогут читать или писать в один и тот же индекс одновременно: доступ к ergo защищен

это решение хорошо изучено

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

{
    QMutexLocker lock(mutex.lock());
    while (numUsedBytes == BufferSize)
    {
         bufferNotFull.wait(&mutex);
         if(shouldStop)return;
    }
}
  • 0
    Вопрос о QMutexLocker, документация говорит мне, что все, что он делает, это блокирует мьютекс, указанный в его конструкторе, и затем разблокируется, когда он уничтожается. Здесь мьютекс никогда не уничтожается во время цикла, поэтому когда мьютекс блокируется и разблокируется для доступа к shouldStop, numUsedBytes, bufferSize и buffer? В основном, почему они должны были также обернуть оператор ожидания в блокировку / разблокировку блоков?
  • 0
    локер разрушается, когда выходит из области видимости, поэтому в моем коде через некоторое время выходит и выходит из фигурных скобок, затем локер разрушается и мьютекс разблокируется
Показать ещё 1 комментарий
1

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

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

while (numUsedBytes == 0)
    bufferNotEmpty.wait(&mutex);

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

В-третьих, прекратить такие потоки, как это сложно, но ваша идея в основном верна. Если основная программа знает, когда пора заканчивать, измените циклы ожидания на что-то вроде:

while (numUsedBytes == 0)
    bufferNotEmpty.wait(&mutex);
    if (TimeToEnd) {unlock(); return;}

и в основном, когда время до конца вы говорите

lock();
TimeToEnd = true;
wakeAll();
unlock();
joinAllThreads();

Если производитель знает, когда пришло время для завершения, то происходит что-то очень похожее, и основная программа просто соединяет все потоки сразу после создания всех потоков.

  • 0
    при тестировании TimeToEnd также не должно быть скобок вокруг тела TimeToEnd .

Ещё вопросы

Сообщество Overcoder
Наверх
Меню