#c# #multithreading #sockets
#c# #многопоточность #сокеты
Вопрос:
У меня есть класс C #, который выполняет бесконечный цикл до тех пор, пока условной переменной не будет присвоено значение true. Есть другой класс, который ожидает сетевого сообщения, и когда сообщение получено, вызывается другой класс для изменения условной переменной на true, чтобы он мог выйти из цикла while. Ожидание сообщения выполняется в отдельном потоке:
Класс-модификатор:
public class Modifier{
Otherclass log;
private static NetworkStream theStream;
private StreamReader theInput;
public Modifier(Otherclass other, NetworkStream str)
{
this.log = other;
theStream = str;
theInput = new StreamReader(theStream);
Thread listenThread = new Thread(new ThreadStart(listen));
listenThread.Start();
}
public void listen()
{
while (true)
{
log.postMessage(theInput.ReadLine());
}
}
}
И другой класс:
public class Otherclass{
bool docontinue = true;
public void postMessage(string input)
{
docontinue = true;
}
public void wait()
{
while(!docontinue)
{
}
}
}
Проблема в том, что программа застревает на while (!docontinue), хотя сообщение отправлено. Я подозреваю, что проблема в том, что переменная docontinue не изменяется, но я не знаю, кроется ли проблема где-то в другом.
Ответ №1:
Здесь возникают различные проблемы —
Первый и прямой ответ на ваш вопрос заключается в том, что вам нужно объявить ваше логическое поле, используя volatile:
private volatile bool doContinue = true;
При этом иметь цикл, который выполняет цикл while без тела, очень плохо — он будет использовать 100% процессора в этом потоке и просто «вращаться» бесконечно.
Гораздо лучший подход к подобным ситуациям — заменить цикл while на WaitHandle, такой как ManualResetEvent. Это позволяет вам дождаться события сброса и блокировать, пока вы не будете готовы продолжить. Вы вызываете Set() для него в другом потоке, чтобы разрешить продолжение выполнения.
Например, попробуйте это:
public class Otherclass{
ManualResetEvent mre = new ManualResetEvent(false);
public void PostMessage(string input)
{
// Other stuff here...
mre.Set(); // Allow the "wait" to continue
}
public void Wait()
{
mre.WaitOne(); // Blocks until the set above
}
}
Ответ №2:
Здесь у вас есть два (потенциально) бесконечных цикла. И на самом деле ничто никогда не вызывает Wait()
Есть ли веская причина, по которой вам нужно тратить циклы в фиктивном цикле внутри метода wait? Какой цели это служит?
Мне кажется, postMessage должен запустить новый поток, который выполнит всю необходимую работу после того, как функция Wait() должна прерваться.
Ответ №3:
Вы можете использовать Volatile
private volatile bool docontinue = true;
Ответ №4:
Попробуйте добавить поток.Переход в спящий режим (100) в вашем цикле. Также рассмотрите возможность использования класса ManualResetEvent.
ОБНОВЛЕНИЕ: я только что проверил, wait() завершается даже без потока.Режим ожидания, изменчивость и другие вещи. Но мое тестовое консольное приложение зависает, потому что функция listen() никогда не заканчивается…
Ответ №5:
Другие люди указывали, что есть лучшие способы сделать это, но я хотел указать на проблему в опубликованном вами коде.
public class Otherclass{
bool docontinue = true;
public void postMessage(string input)
{
docontinue = true;
}
public void wait()
{
while(!docontinue)
{
}
}
}
docontinue
значения не изменяются. Оно начинается как true, и вы устанавливаете его в true при отправке сообщения. Кроме того, у вас есть not в вашем предложении while, поэтому цикл никогда не должен выполняться, так как !docontinue
всегда имеет значение false .