Добрый день!
Есть приложение, которое опрашивает некий прибор по COM порту в потоке и выводит прочитанные данные в GUI.
Опрос делается в бесконечном цикле с проверкой "флажка" ManualResetEvent для сигнализации завершения и поднятия другого "флажка" для сигнализации завершения, как-то так:
В приложении есть чекбокс по клику на который я лолжен остановить поток чтения — т.е. поднимаю флажок остановки опроса и жду флажка завершения:
threadStop.Set();
threadEnd.WaitOne(2000, true);
Проблема собственно в том, что threadEnd устанавливается не всегда. И я не могу понять, почему так происходит. Время в ожидании 2 секунды можно поменять на любое другое — ничего не поменяется. Просто после threadStop.Set() я буду находится в функции клика чекбокса, а функции опроса не будет передаваться управление. Может кто-нибудь даст мне совет?
Для ожидания завершения потока есть Thread.Join.
А вообще в GUI-потоке нельзя ничего "ждать". Почему не воспользоваться делегатом + Invoke?
А>Просто после threadStop.Set() я буду находится в функции клика чекбокса, а функции опроса не будет передаваться управление.
Как ей может не передаваться управление, если она работает в другом потоке? Покажите больше кода.
А>Для ожидания завершения потока есть Thread.Join. А>А вообще в GUI-потоке нельзя ничего "ждать". Почему не воспользоваться делегатом + Invoke?
А>>Просто после threadStop.Set() я буду находится в функции клика чекбокса, а функции опроса не будет передаваться управление.
А>Как ей может не передаваться управление, если она работает в другом потоке? Покажите больше кода.
Спасибо, что откликнулись!
Прибил свой второй "флажок", теперь завершение такое:
threadStop.Set();
th.Join();
Но иногда программа зависает на th.Join() — т.е. аналогичная ситуация. Я не могу понять, почему когда выставил "флажок" threadStop, до функции опроса, где этот флажок анализируется, чтобы выйти из вечного цикла, дело не доходит (проверял точками останова и отладочной печатью). Проблема в том, что я делаю Join() в GUI-потоке? Но все равно не могу осмыслить отчего блокируюсь на th.Join.
Re[2]: Корректное ожидание завершения потока
От:
Аноним
Дата:
20.04.12 12:34
Оценка:
Здравствуйте, rumatavz, Вы писали:
R>
R>threadStop.WaitOne(0, true)
R>
R>Второй аргумент true на всякий случай или есть основания? Если есть то этого куска кода явно недостаточно.
R>Если запустить бесконечный цикл ровно в том виде как на форме проблема продолжает наблюдаться?
Второй аргумент на всякий случай — пробовал сначала без него, но без изменений.
Закомментил лишнее в цикле — проблема исчезла...
Но там не так много, из таблицы делегатов по очереди выбираются функции, которые читают из com порта определенные параметры:
А>а функции опроса не будет передаваться управление.
Тот поток, в котором крутится бесконечный цикл виснет на какой нибудь строке или продолжает крутиться?
Re[2]: Корректное ожидание завершения потока
От:
Аноним
Дата:
20.04.12 13:36
Оценка:
Здравствуйте, rumatavz, Вы писали:
А>>а функции опроса не будет передаваться управление.
R>Тот поток, в котором крутится бесконечный цикл виснет на какой нибудь строке или продолжает крутиться?
Поток с бесконечным циклом не виснет, виснет в функции клика по чекбоксу, где я останавливаю бесконечный цикл.
Переписал событие клика через Invoke — не помогло:
Здравствуйте, rumatavz, Вы писали:
А>>а функции опроса не будет передаваться управление.
R>Тот поток, в котором крутится бесконечный цикл виснет на какой нибудь строке или продолжает крутиться?
Я выше не правильно выразился. Поток состоит из функции опроса с бесконечным циклом — так вот при выставлении из другого места (нажатие чекбокса) флажка остановки, судя по отладочным сообщениям, я больше в ту функцию опроса никогда не попадаю. И это мне совсем не понятно.
А>Я выше не правильно выразился. Поток состоит из функции опроса с бесконечным циклом — так вот при выставлении из другого места (нажатие чекбокса) флажка остановки, судя по отладочным сообщениям, я больше в ту функцию опроса никогда не попадаю. И это мне совсем не понятно.
Так вот мой вопрос и состоит в том, что делает этот самый поток.
До нажатия на чекбокс поставьте брейк в цикле и в окне Debug->Windows->Threads посмотрите ИД потока.
После нажатия на чекбокс найдите этот же поток в том окне и посмотрите его Call Stack (Debug->Windows->Call Stack)
Колл стэк приведите плз.
Re[4]: Корректное ожидание завершения потока
От:
Аноним
Дата:
20.04.12 14:13
Оценка:
Здравствуйте, rumatavz, Вы писали:
А>>Я выше не правильно выразился. Поток состоит из функции опроса с бесконечным циклом — так вот при выставлении из другого места (нажатие чекбокса) флажка остановки, судя по отладочным сообщениям, я больше в ту функцию опроса никогда не попадаю. И это мне совсем не понятно.
R>Так вот мой вопрос и состоит в том, что делает этот самый поток. R>До нажатия на чекбокс поставьте брейк в цикле и в окне Debug->Windows->Threads посмотрите ИД потока. R>После нажатия на чекбокс найдите этот же поток в том окне и посмотрите его Call Stack (Debug->Windows->Call Stack) R>Колл стэк приведите плз.
Спасибо за советы и желание помочь! Я в отладки многопоточности совсем не силен. Когда приложение подвисает на Join поток сидит в функции SetText.
Эта функция служит для "безопасного" отображения в GUI принятых данных из COM порта, выглядит так:
delegate void SetTextInvoker(object box, object text);
public void SetText(object box, object text)
{
if (InvokeRequired)
{
Invoke(new SetTextInvoker(SetText), box, text);
return;
}
(box as TextBox).Text = (string)text;
}
Колл стэк выглядит так:
[In a sleep, wait, or join]
[External Code]
> AutoCalib.exe!AutoCalib.ModBusPolling.PidPollingForm.test() Line 305 + 0x10 bytes C#
[External Code]
AutoCalib.exe!AutoCalib.ModBusPolling.PidPollingForm.chbxStatePID_MouseClick(object sender = {Text = Cannot evaluate expression because the current thread is in a sleep, wait, or join CheckState = Checked}, System.Windows.Forms.MouseEventArgs e = {X = 0x00000006 Y = 0x0000000b Button = Left}) Line 297 + 0x29 bytes C#
[External Code]
AutoCalib.exe!AutoCalib.frmMain.регистрыПИДToolStripMenuItem_Click(object sender = {System.Windows.Forms.ToolStripMenuItem}, System.EventArgs e = {System.EventArgs}) Line 238 + 0xa bytes C#
[External Code]
AutoCalib.exe!AutoCalib.Program.Main() Line 17 + 0x1d bytes C#
[External Code]
Re[4]: Корректное ожидание завершения потока
От:
Аноним
Дата:
20.04.12 14:34
Оценка:
Добавлю к вышесказанному, что в функции SetText в Invoke() входит, но от туда уже не выходит. Поэтому потоку и не передается управление. Но почему так происходит, мне не хватает опыта понять.
Здравствуйте, Аноним, Вы писали:
А>Добрый день! А>Есть приложение, которое опрашивает некий прибор по COM порту в потоке и выводит прочитанные данные в GUI.
А почему не BackgroundWorker? У него довольно удобные события для таких сценариев.
Re[2]: Корректное ожидание завершения потока
От:
Аноним
Дата:
20.04.12 14:44
Оценка:
Здравствуйте, Flem1234, Вы писали:
F>Здравствуйте, Аноним, Вы писали:
А>>Добрый день! А>>Есть приложение, которое опрашивает некий прибор по COM порту в потоке и выводит прочитанные данные в GUI. F>А почему не BackgroundWorker? У него довольно удобные события для таких сценариев.
Видимо придется смотреть в сторону BackgroundWorker, просто считал, что я реализую аналогичный ему функционал. Теперь уже из-за спортивного интереса хочется понять, что не так...
Здравствуйте, Аноним, Вы писали:
А>Видимо придется смотреть в сторону BackgroundWorker, просто считал, что я реализую аналогичный ему функционал. Теперь уже из-за спортивного интереса хочется понять, что не так...
Он простой как валенок, этот воркер, немного громоздкий только.
Лучше выложите полный пример, а то по таким данным оч.тяжело догадаться, что происходит.
Re[4]: Корректное ожидание завершения потока
От:
Аноним
Дата:
20.04.12 14:58
Оценка:
Здравствуйте, Flem1234, Вы писали:
F>Здравствуйте, Аноним, Вы писали:
А>>Видимо придется смотреть в сторону BackgroundWorker, просто считал, что я реализую аналогичный ему функционал. Теперь уже из-за спортивного интереса хочется понять, что не так...
F>Он простой как валенок, этот воркер, немного громоздкий только. F>Лучше выложите полный пример, а то по таким данным оч.тяжело догадаться, что происходит.
Полный пример еще более сложен для понимания, я пытался как можно яснее и без перегруза информации изложить суть... Благодаря помощи выше вроде выяснилось, что Invoke, который вызывается в функции установки текста в текстбокс, иногда улетает в астрал. И в этом случаи я навсегда зависаю при ожидании завершения потока. Осталось понять, отчего так ведет себя Invoke...
public void SetText(object box, object text)
{
if (InvokeRequired)
{
Invoke(new SetTextInvoker(SetText), box, text);
return;
}
(box as TextBox).Text = (string)text;
}
[/c#]
threadStop.Set();
threadEnd.WaitOne(2000, true);
Теперь смотрим
В потоке ГУИ выполняется строка threadEnd.WaitOne(2000, true) теперь поток ГУИ не может ничего сделать пока не выполнится эта строка.
У потока с циклом 2 варианта: SetText уже выполнилась в данной итерации. Тогда всё ок — поток вывалится на брейка.
А вот если после то выполнится SetText. Эта функция в свою очередь вызовет Invoke. Invoke начнет ждать пока поток ГУИ будет свободен.
А поток ГУИ будет ждать threadEnd.Set(). Дедлок на лицо.
Технически решить это можно вызвав BeginInvoke в потоке цикла.
Но на мой взгляд тут архитектурная ошибка. Вы в ГУИ ждете, пока другой поток что то сделает — в данном случае завершится.
Вряд ли вам нужно именно подвивание интерефейса до завершения потока.
По этому я бы сделал бы так:
class ClassWhereLoopRuns : IDisposable
{
private volatile bool _needShutdown;
public void ShutDown()
{
_needShutdown = true;
}
? void Loop()
{
while(_needShutdown)
}
...
RaiseEventTextChanged();
...
}
}
RaiseEventShutDownDone();
}
class SomeControl
{
void OnClick(...)
{
_classWhereLoopRuns.ShutDown();
}
void OnTextChanged(...) //OnShutDownDone работает аналогично
{
Invoke(DoOnTextChanged) //в DoOnTextChanged работа с ГУИ
}
}
ClassWhereLoopRuns имеет два события на которые подписывается контрол. Синхронизацию осуществляет сам контрол, остальные ничего не знают о ГУИшных потоках. Когда ClassWhereLoopRuns завершает работу он говорит об этом ГУИ, а не ГУИ ждет завершения.
Re[2]: Корректное ожидание завершения потока
От:
Аноним
Дата:
20.04.12 21:25
Оценка:
Здравствуйте, rumatavz, Вы писали:
R>Технически решить это можно вызвав BeginInvoke в потоке цикла. R>Но на мой взгляд тут архитектурная ошибка. Вы в ГУИ ждете, пока другой поток что то сделает — в данном случае завершится. R>Вряд ли вам нужно именно подвивание интерефейса до завершения потока. R>По этому я бы сделал бы так:
R>
R>ClassWhereLoopRuns имеет два события на которые подписывается контрол. Синхронизацию осуществляет сам контрол, остальные ничего не знают о ГУИшных потоках. Когда ClassWhereLoopRuns завершает работу он говорит об этом ГУИ, а не ГУИ ждет завершения.
Спасибо за предложенное решение моей проблемы! Обязательно в понедельник пересмотрю свою архитектуру — надеюсь поможет.
Здравствуйте, Аноним, Вы писали:
F>>Он простой как валенок, этот воркер, немного громоздкий только. F>>Лучше выложите полный пример, а то по таким данным оч.тяжело догадаться, что происходит. А>Полный пример еще более сложен для понимания, я пытался как можно яснее и без перегруза информации изложить суть... Благодаря помощи выше вроде выяснилось, что Invoke, который вызывается в функции установки текста в текстбокс, иногда улетает в астрал. И в этом случаи я навсегда зависаю при ожидании завершения потока. Осталось понять, отчего так ведет себя Invoke...
By Design.
Invoke отправляет сообщение в UI-поток и пытается дождаться ответа на него. Deadlock.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Корректное ожидание завершения потока
От:
Аноним
Дата:
23.04.12 07:22
Оценка:
Большое человеческое спасибо rumatavz! Благодаря вашим разъяснением все работает, как и хотелось.
И я теперь имею понимание, почему не работало, что не менее важно.
PS: Проглядываю свои прошлые программки в связи с открывшимися знаниями на предмет исправления.