Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #5 : 27-11-2013 22:52 » |
|
В .NET есть ManualResetEvent и AutoResetEvent. Это как раз объекты синхронизации для таких случаев. Оба работают таким образом, что если флаг события стоит, то любые операции Wait не ждут. Если же флаг события сброшен, то любые операции Wait останавливают вызвавший их поток до установки флага. Разница между ними в том, что в первом случае флаг нужно сбрасывать вручную - т.е. если на ожидании висит много потоков, они все или несколько из них могут пройти Wait, прежде чем один из них сбросит флаг; во втором случае факт прохождения одного потока приводит к автоматическому сбрасыванию флага, и потоки могут проходить через ожидание события строго по очереди.
Однако из формулировки задачи судя по жалобе на зависания вовсе неясно, что код с циклом выполняется в отдельном потоке, и что это именно так нужно. Поэтому присоединяюсь к предложению Dale рассмотреть возможность создания собственного события, которое срабатывает при каком-то условии (кстати, непонятно, что это? started - уже/ранее запущенный, starting - запускающийся, startRequired - требующий/запрашивающий запуска). И, соответственно, все желающие могут на это событие подписаться. Возможно даже и переменной не понадобится, если подробно описать задачу.
Что касается фонового исполнения, то упомянутый background work - это лишь обёртка над банальным потоком (скорее всего из системного пула потоков), у которого установлено свойство IsBackground. Отличается он от обычного потока тем, что не способен удерживать процесс в работающем состоянии. Т.е. как только в процессе закончили работу все обычные потоки, фоновые потоки уничтожаются автоматически. Это накладывает на фоновые потоки некоторые ограничения: например, не стоит в них работать с какими-то критическими ресурсами, требующими корректного освобождения. Кроме того, фоновые потоки работают с более низким приоритетом, чем обычные. Ну и поскольку речь идёт о потоке, то, разумеется, окончание работы процедуры потока приводит к завершению самого потока. Таким образом, претензии "только один раз работает" не принимаются: цикл писать всё равно нужно. Альтернативными циклу вариантами являются использование периодического запуска по таймеру и создание схемы каскада асинхронных вызовов (аналог рекурсивного вызова функции, только из-за асинхронного характера не накапливается стек вызовов).
По поводу того, что работает или не работает Label. Элементы пользовательского интерфейса - будь то Windows.Forms или WPF - работают в модели STA, и я понятия не имею, каким боком в недрах .NET проявляются эти вещи, унаследованные от COM. Но суть в том, что в STA модели доступ к объектам имеет только один поток - тот, в котором они созданы, и это позволяет не заботиться о синхронизациях и в общем-то ускоряет работу. Именно поэтому попытка прямого обращения к Label и любому другому элементу пользовательского интерфейса из другого потока просто как к объекту в памяти закончится плачевно. Чтобы выполнять эту работу в WPF существует специальный объект Dispatcher (доступный через свойство любого элемента пользовательского интерфейса), через который осуществляется marshaling вызовов между потоками. В частности, из одного потока можно синхронно или асинхронно выполнить код, работающий с данными другого потока. В случае синхронного вызова это происходит так: главный поток пользовательского интерфейса, разумеется, крутится в главном цикле приложения - обрабатывает сообщения Windows; когда другой поток делает вызов, он переходит в режим ожидания отклика главного потока; главный поток, когда заканчивает обработку очередного сообщения и переходит в состояние анализа следующего задания/сообщения, обнаруживает в очереди запрос к себе и выполняет код запроса, после чего продолжает работать в цикле; вызывающий поток, получив результат обработки запроса, тоже продолжает работу. В случае асинхронного вызова вызывающий поток не ждёт ничего такого, а просто продолжает работу, оставив свой запрос в очереди на исполнение главного потока. Реализуется это через стандартный шаблон асинхронного вызова Begin-End.
|