Вопрос или проблема
Этот код должен остановиться на 15 символах (строка), но он этого не делает и продолжает работать бесконечно. Главная проблема этого кода заключается в том, что логика условия переменной не позволяет всем потокам распознавать, когда достигнуто целевое значение max_count 15, что приводит к бесконечному выполнению программы.
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>
using namespace std;
class Monitor
{
private:
string line = "";
mutex mtx;
condition_variable cv;
int vowel_count = 0;
int max_count = 0;
public:
void AddA(char symbol)
{
unique_lock<mutex> lock(mtx);
cv.wait(lock, [this] { return vowel_count < 3; });
line += symbol;
vowel_count++;
max_count++;
cv.notify_all();
}
void AddBC(char symbol)
{
unique_lock<mutex> lock(mtx);
cv.wait(lock, [this] { return vowel_count >= 3; });
line += symbol;
max_count++;
vowel_count = 0;
cv.notify_all();
}
string getLine()
{
unique_lock<mutex> lock(mtx);
return line;
}
int getVowelCount()
{
unique_lock<mutex> lock(mtx);
return vowel_count;
}
int getMaxCount()
{
unique_lock<mutex> lock(mtx);
return max_count;
}
};
void First(Monitor& monitor)
{
while (true)
{
monitor.AddA('A');
if (monitor.getMaxCount() == 15)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void Second(Monitor& monitor)
{
while (true)
{
monitor.AddBC('B');
if (monitor.getMaxCount() == 15)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void Third(Monitor& monitor)
{
while (true)
{
monitor.AddBC('C');
if (monitor.getMaxCount() == 15)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void Print(Monitor& monitor)
{
while (true)
{
cout << monitor.getLine() << endl;
if (monitor.getMaxCount() == 15)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main()
{
Monitor monitor;
thread t1(First, ref(monitor));
thread t2(Second, ref(monitor));
thread t3(Third, ref(monitor));
thread t4(Print, ref(monitor));
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
Я пробовал различные варианты, например, изменял условие на больше или равно 15, но это еще больше усложняет ситуацию.
Ответ или решение
Проблема с Мониторами и Мьютексами в C++
Проблема, о которой идет речь, связана с бесконечным циклом в предоставленном фрагменте кода C++, который использует мьютексы и условные переменные для синхронизации потоков. Основная задача заключается в том, чтобы убедиться, что программа корректно останавливается, когда длина линии достигает 15 символов.
Анализ кода
Класс Monitor
существует для управления добавлением символов к строке с учетом определенных условий:
- Потоки
First
добавляют символы ‘A’, если количество гласных (в данном случае — только ‘A’) меньше 3. - Потоки
Second
иThird
добавляют символы ‘B’ и ‘C’ соответственно, если количество гласных равно или превышает 3.
Однако, как указано в описании проблемы, код продолжает выполняться бесконечно, и основная причина заключается в логике условных переменных, которые не позволяют всем потокам корректно узнавать, когда общий счетчик символов достиг 15.
Задача: Улучшение логики синхронизации
Для решения этой проблемы важно изменить способ, которым потоки синхронизируются и ожидают добавления символов. Вот несколько рекомендаций:
-
Ограничить общее количество символов: Каждый поток должен не только проверять условие, относящееся к своему типу символа, но также должна быть проверка на общий
max_count
. -
Использование двух условных переменных: Можно ввести дополнительные условия для проверки, достигнуто ли максимальное количество символов и позволить потокам завершать свою работу корректно.
-
Использование
cv.notify_all()
: Это важно для того, чтобы все потоки знали о возможности перезапуска, когда один из потоков завершает свою работу.
Модифицированный код
Вот предложенный модифицированный код, который корректно обрабатывает состояние с максимальным количеством символов:
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>
using namespace std;
class Monitor {
private:
string line = "";
mutex mtx;
condition_variable cv;
int vowel_count = 0;
int max_count = 0;
public:
void AddA(char symbol) {
unique_lock<mutex> lock(mtx);
cv.wait(lock, [this]() { return vowel_count < 3 && max_count < 15; });
line += symbol;
vowel_count++;
max_count++;
cv.notify_all();
}
void AddBC(char symbol) {
unique_lock<mutex> lock(mtx);
cv.wait(lock, [this]() { return vowel_count >= 3 && max_count < 15; });
line += symbol;
max_count++;
vowel_count = 0;
cv.notify_all();
}
string getLine() {
unique_lock<mutex> lock(mtx);
return line;
}
int getMaxCount() {
unique_lock<mutex> lock(mtx);
return max_count;
}
bool reachedMaxCount() {
unique_lock<mutex> lock(mtx);
return max_count >= 15;
}
};
void First(Monitor& monitor) {
while (!monitor.reachedMaxCount()) {
monitor.AddA('A');
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void Second(Monitor& monitor) {
while (!monitor.reachedMaxCount()) {
monitor.AddBC('B');
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void Third(Monitor& monitor) {
while (!monitor.reachedMaxCount()) {
monitor.AddBC('C');
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void Print(Monitor& monitor) {
while (!monitor.reachedMaxCount()) {
cout << monitor.getLine() << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main() {
Monitor monitor;
thread t1(First, ref(monitor));
thread t2(Second, ref(monitor));
thread t3(Third, ref(monitor));
thread t4(Print, ref(monitor));
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
Заключение
Изменения в синхронизации потоков обеспечивают более надежное завершение работы программы, когда общее количество символов достигает 15. Использование методов cv.wait()
и cv.notify_all()
в контексте общего состояния потока позволяет избежать бесконечного цикла. Такие улучшения делают вашу программу более эффективной и безопасной.
Оптимизация кода с точки зрения многопоточности является критически важной в современных приложениях. Четкое понимание работы с мьютексами и условными переменными помогает создать качественный и безопасный код.