2013-11-29 2 views
0

Мне пришлось реализовать еще две асинхронные потоки в моей основной теме.Winforms-Timer: запущен в asynch thread omits tick event в ui thread

Один (T_A1) извлекает некоторые данные db и возвращает, ui все еще может быть активным и получать пользовательские взаимодействия (возможно, для изменения в будущем обратно в один поток) и другой поток (T_A2), который опроса некоторых аппаратных средств для специального значения , У меня также есть таймер на моем winforms-ui, простом объекте windows.forms.Timer.

Когда T_A2 (который запускается в собственном объекте) выдает событие в поток ui для его обработки, он все еще находится в потоке asynch, но обрабатывается внутри mainform. В этом обработчике событий я запускаю таймер. (Просто через timer.Start();)

Я зарегистрировал событие tick, но я его не понимаю, он просто никогда не встречается, поэтому у меня нет записи в журнале.

НО, когда я запускаю таймер с помощью Invoke/BeginInvoke, я начинаю запуск таймера, который будет выполняться в ui. И затем происходит событие tick, и я получаю запись в журнале.


Мой вопрос: ПОЧЕМУ?

Если я не использую Invoke/BeginInvoke, я запускаю таймер в потоке, который выбрасывает событие (T_A2), поэтому в этом потоке также ожидается появление галочки, но поток уже умер?

Или что может быть причиной?

+0

Я хорошо читаю код, чем прохождение, большинство из них здесь согласятся со мной! Поэтому отправляйте какой-то код, мы не можем принимать и отвечать. –

+0

Если вы используете 'System.Windows.Forms.Timer', то он будет всегда выполняться только в потоке пользовательского интерфейса. Не уверен, что об этом вы спрашиваете? Таймеры не будут запускаться в потоке, это запущено, я понятия не имею, почему, по вашему мнению, он срабатывает в том же потоке, который начался? –

+0

Запустите свой таймер в потоке пользовательского интерфейса и запустите новый поток в своем Tick-событии, чтобы выполнить опрос. – Koen

ответ

2

Почему? простой, резьба опасно. У него есть умение для не делать то, что вы надеетесь, он будет делать.

Многие классы графического интерфейса в Winforms не являются потокобезопасными, вы должны использовать команду Control.BeginInvoke, чтобы вы изменяли свои свойства или вызывали их методы в том же самом потоке, который создал объекты. Это довольно фундаментальное ограничение, создавая потокобезопасный код: сложно.

Даже простые классы в .NET не являются потокобезопасными, ни один из классов коллекции не является. У вас есть широта с, скажем, списком <>, вы можете сделать его потокобезопасным, тщательно используя ключевое слово lock, чтобы гарантировать, что объект List доступен только из одного потока. Все еще довольно сложно сделать правильно.

Это перестает быть практичным, если класс не прост и имеет нетривиальные взаимодействия с другими классами и операционной системой. Немногие классы в .NET имеют тяжелый контроль, у него сотни методов и свойств.Таким образом, сама Microsoft просто заявила о том, что сделать ее потокобезопасной невозможной, например, со списком <>, и нет никакой надежды на то, что вы можете добавить достаточно блокировки, чтобы сделать ее потокобезопасной. Тупик практически гарантирован. Microsoft также не предоставила вам достаточный доступ к внутренним компонентам, чтобы ввести требуемую блокировку, они не думали, что это того стоит.

Значит, вы должны использовать объекты из одной и той же темы, чтобы обеспечить безопасность, они предоставили вам необходимые инструменты, чтобы сделать это легко. В том числе BeginInvoke и всевозможные дополнительные справки, такие как BackgroundWorker, TaskScheduler.FromCurrentSynchronizationContext и ключевые слова async/await. И InvalidOperationException вы получаете, когда делаете это неправильно, очень полезное исключение.

Специфическая деталь реализации класса Timer, с которым вы столкнулись, состоит в том, что это довольно простой класс, а фактически - это потокобезопасный. Запуск или остановка на рабочем потоке работает отлично, и это безопасно. Обратите внимание, что вы не получили исключение InvalidOperationException, которое вы обычно получаете, когда нарушаете требования к потоку. К сожалению, это зависит от деталей реализации, о которых вы не заботились, и никогда не будет. Событие Tick добавляется в цикле диспетчера, который вы получаете из Application.Run(). И вы не вызывали Application.Run() в своем рабочем потоке. Поэтому таймер никогда не гаснет.

Правильное решение здесь не call Application.Run() для вашего рабочего, это одно решение, которое дает вам две новые проблемы. У вас уже есть отличный поток, который называется Application.Run(). Используйте Control.BeginInvoke(), чтобы использовать его.

+0

Whooha, это ответ. THX очень, уважайте и продолжайте так. Отмечено как «Ответ». Но можете ли вы сказать мне, где получить такую ​​подробную информацию, если не иметь дело с внутренними сетями .net в течение нескольких десятилетий? – icbytes

+0

Короче, потратив на себя три десятилетия, которые мне потребовались, вы получите это на SO, конечно :) –

+0

WOWOWO. Сейчас я занимаюсь около 5 лет, с .net 1.1, 2.0. Вот и все. Будем надеяться, я получу 10% от вашего опыта в ближайшие 5 лет. Увидимся в следующий раз, пока. – icbytes

1

Простой ответ заключается в следующем:

System.Windows.Forms.Timer тесно связан с контуром сообщений приложения. Это полностью зависит от контура сообщения, и как таковой он работает только в основном потоке пользовательского интерфейса. Он не создает свой собственный поток. В вашем потоке T_A2 нет цикла сообщений, поэтому он просто умирает - и даже если ваш поток продолжается, это не вызовет каких-либо событий таймера.

Если вы хотите, чтобы таймер не был связан с контуром сообщения, вы должны использовать System.Timers.Timer, который работает совершенно по-другому, а также будет работать в вашем сценарии.