Я отлично знаю, что делаю многопоточность, и, несмотря на массивную головную боль, я все еще считаю ее невероятно увлекательной. Я использую qt и изучаю учебные пособия, и нашел, что он отлично применим к моей ситуации, которая передает потоки данных в круговой буфер, один поток записывает в него, один поток считывает из него. Это учебник здесь. Сначала позвольте мне спросить вас, выбрал ли я правильный метод в ожидании.
Я решил не использовать семафоры, потому что размер буфера может измениться в любой момент из основного потока, что приведет к перераспределению и перенастройке индексов чтения/записи, а изменение семафоров не так просто. Я не хотел размещать сравнение в цикле для того, нужно ли выполнять тело цикла, потому что он должен будет делать это снова и снова, пока он не сможет вместить больше данных в буфер, поэтому условия ожидания были моим естественным решением,
Во-вторых, я немного смущен тем, что условия ожидания делают в своих примерах. Похоже, что они блокируют мьютексы, проверяют условие ожидания и блокируют, продолжаются, когда выполняется условие, а затем отпирают мьютекс прямо там. Разве это не означает, что доступ к буфере для записи полностью незащищен в этот момент?
В-третьих, я немного потеряю, как закончить эти потоки, когда мой основной поток хочет остановиться. Я не могу использовать условие ожидания, потому что я хочу, чтобы они запускались до тех пор, пока хочет основной поток, и я не думаю, что могу сигнализировать по потокам, чтобы сказать, чтобы он ушел. Единственный способ сообщить потоку прекратить выполнение, поместить где-нибудь постоянную проверку bool, расположенную в основном потоке, каждый цикл, а затем дождаться, когда потоки будут удалены в деструкторе? Проблема в том, что с условиями ожидания я должен был сначала установить bool для выхода, а затем разбудить оба потока. Я действительно смущен тем, что здесь делать, и любой совет приветствуется.
доступ к буферам защищен в силу того, как выбран индекс данных:
0
1
0
в частности, единственный способ i % buffersize
от потребителя будет таким же, как i % buffersize
от производителя между критическими разделами, это если numUsedBytes == 0
и вы являетесь производителем или numUsedBytes == buffersize
и вы являетесь потребителем
видя это, вы должны быть в состоянии рассуждать о том, что ни производитель, ни потребитель не смогут читать или писать в один и тот же индекс одновременно: доступ к ergo защищен
есть несколько проблем с этой реализацией, хотя: если проверка недостаточно, если условие разбудится по какой-либо причине. они должны использовать QMutexLocker, поэтому исключения или ранние возвращения не будут все испортить:
{
QMutexLocker lock(mutex.lock());
while (numUsedBytes == BufferSize)
{
bufferNotFull.wait(&mutex);
if(shouldStop)return;
}
}
Во-первых, да, условия ожидания - правильный инструмент для этой работы.
Их использование условий ожидания не самое лучшее, потому что иногда поток проснулся преждевременно. Их оператор if должен быть циклом while, например
while (numUsedBytes == 0)
bufferNotEmpty.wait(&mutex);
Во-вторых, производитель и потребитель тщательно пишутся так, что производитель владеет некоторыми байтами в буфере, а потребитель владеет другими. То есть, потребитель владеет частью с неисследованными данными в нем, а сам продукт - неиспользованной частью. Вот почему они могут безопасно читать или писать байты в буфере, потому что они гарантируют, что другой поток не использует этот байт.
В-третьих, прекратить такие потоки, как это сложно, но ваша идея в основном верна. Если основная программа знает, когда пора заканчивать, измените циклы ожидания на что-то вроде:
while (numUsedBytes == 0)
bufferNotEmpty.wait(&mutex);
if (TimeToEnd) {unlock(); return;}
и в основном, когда время до конца вы говорите
lock();
TimeToEnd = true;
wakeAll();
unlock();
joinAllThreads();
Если производитель знает, когда пришло время для завершения, то происходит что-то очень похожее, и основная программа просто соединяет все потоки сразу после создания всех потоков.
TimeToEnd
также не должно быть скобок вокруг тела TimeToEnd
.