Проблема с несколькими производителями / потребителями и кодом критического раздела
-
05-07-2019 - |
Вопрос
Я пытаюсь решить проблему с несколькими производителями / потребителями на C, но она работает не так, как ожидалось.Ниже приведен некоторый псевдокод для представления моей реализации.
Thread thread1;
Thread thread2;
Thread thread3;
Data data1;
Mutex data1_mutex;
Semaphore data1_empty;
Semaphore data1_fill;
Data data2;
Mutex data2_mutex;
Semaphore data2_empty;
Semaphore data2_fill;
thread1()
{
// creates data and places it into data1.
wait(data1_empty);
lock(data1_mutex);
// critical section
unlock(data1_mutex);
post(data1_fill);
}
thread2()
{
// Removes data from data1, processes it, and places it into data2.
// data1
wait(data1_fill);
lock(data1_mutex);
// data2
wait(data2_empty);
lock(data2_mutex);
// critical section
// data2
unlock(data2_mutex);
post(data2_fill);
// data1
unlock(data1_mutex);
post(data1_empty);
}
thread3()
{
// Removes data from data2, prints its results, and removes it.
wait(data2_fill);
lock(data2_mutex);
// critical section
unlock(data2_mutex);
post(data2_empty);
}
Однако при таком решении data1 заполнится, но thread2 заблокируется и никогда не запустится.Что-то не так с моей реализацией?
ПРАВКА №1
Одна из проблем, которую я обнаружил, заключалась в том, что мой второй мьютекс создавался некорректно.Я не знаю, что в этом плохого, поэтому я просто использую первый мьютекс для всех потоков.Есть также кое-что еще, что я сделал, чтобы заставить его работать, поэтому я обновлю свой псевдокод, чтобы отразить это позже, когда у меня будет свободная минута.
Решение
Убедитесь, что вы сначала отправили data1_empty
и data2_empty
.
Другие советы
Если вы используете какой-то тип очереди для Data
, вы должны быть в состоянии полностью удалить "пустые" семафоры, если только вы не пытаетесь навязать условие, что каждый Data
глубина очереди строго равна 0 или 1.Если вы используете локальную переменную в thread2
, вы можете уменьшить размер критической секции.
Затем код становится чем-то вроде этого:
thread1() {
//Wait for data to put in the queue (i.e. a blocking socket read)
lock(data1_mutex);
data1.push(someData);
unlock(data1_mutex);
post(data1_fill);
}
thread2() {
DataType dataElement;
wait(data1_fill);
lock(data1_mutex);
dataElement = data1.pop();
unlock(data1_mutex);
lock(data2_mutex);
data2.push(dataElement);
unlock(data2_mutex);
post(data2_fill);
}
thread3() {
DataType dataElement;
wait(data2_fill);
lock(data2_mutex);
dataElement = data2.pop();
unlock(data2_mutex);
//do something with dataElement here
}
Что сказал Вранг-Вранг, и постарайтесь не удерживать блокировку для data_1, пока вы ожидаете data_2_empty. Этого можно добиться, сохранив альтернативный буфер для data_1 и data_2, который вы меняете. Thread_2 обменивает данные_1 при обработке их в data_2, thread_3 обменивает данные_2 при их обработке. Ваш текущий псевдокод позволит потоку 1 и потоку 3 работать одновременно, но не позволит потоку 2 выполняться одновременно с любым другим.