Как разблокировать потоки, которые вызвали метод WaitOne для объекта AutoResetEvent?
Ниже приведен класс, который имеет метод "SomeMethod" , который иллюстрирует мою проблему.
class SomeClass
{
AutoResetEvent theEvent = new AutoResetEvent(false);
// more member declarations
public void SomeMethod()
{
// some code
theEvent.WaitOne();
// more code
}
}
Метод разработан для обеспечения потокобезопасности и будет вызываться в разных потоках. Теперь мой вопрос заключается в том, как можно разблокировать все потоки, которые вызывали метод "WaitOne" на объекте "theEvent" в любой момент времени? Это требование часто возникает в моем дизайне, потому что я должен быть в состоянии грациозно остановить и запустить мою многопоточную программу. Мне кажется, что довольно просто запустить многопоточную программу, но сложно остановить ее.
Вот что я пробовал до сих пор, что, видимо, работает. Но является ли это стандартным подходом?
public void UnblockAll()
{
do
{
theEvent.Set();
} while (theEvent.WaitOne(0));
}
Метод "UnblockAll" является членом класса "SomeClass". Используемая здесь методика основана на документации MSDN метода WaitOne. Я цитирую соответствующую часть приведенной ниже документации:
Если миллисекундыTimeout равно нулю, метод не блокируется. Он проверяет состояние дескриптора ожидания и немедленно возвращается.
В цикле do..while я вызываю метод Установить. Это освобождает один поток, который может быть заблокирован из-за вызова метода WaitOne (закодированного внутри метода "SomeMethod" ). Затем я проверяю состояние объекта theEvent, чтобы узнать, сигнализировало ли оно. Этот тест выполняется путем вызова перегруженной версии метода WaitOne, который принимает параметр тайм-аута. Аргумент, который я использую, когда я вызываю метод WaitOne, равен нулю, что в соответствии с документацией приводит к тому, что вызов немедленно возвращается с логическим значением. Если возвращаемое значение истинно, объект 'theEvent' находился в сигнальном состоянии. Если по вызову метода WaitOne в методе "SomeMethod" был заблокирован хотя бы один поток, вызов метода "Set" (закодированный внутри метода "UnblockAll" ) разблокирует его. Следовательно, вызов метода "WaitOne" в конце инструкции do..while в методе "UnblockAll" возвращает false. Возвращаемое значение истинно только в случае, если потоки не заблокированы.
Является ли приведенное выше обоснование правильным, и правильно ли это, является ли метод стандартным способом решения моей проблемы? Я пытаюсь использовать это решение в первую очередь на платформе .net compact-framework 2.0.
Ответы
Ответ 1
У вас есть три жизнеспособных варианта. У каждого есть свои преимущества и недостатки. Выберите тот, который лучше всего подходит для вашей конкретной ситуации.
Вариант 1 - опросить WaitHandle
.
Вместо того, чтобы делать неопределенный блокирующий вызов, используйте один с таймаутом и восстанавливайте блок, если запрос на завершение не был предоставлен.
public void SomeMethod()
{
while (!yourEvent.WaitOne(POLLING_INTERVAL))
{
if (IsShutdownRequested())
{
// Add code to end gracefully here.
}
}
// Your event was signaled so now we can proceed.
}
Вариант 2 - используйте отдельный WaitHandle
для запроса завершения работы
public void SomeMethod()
{
WaitHandle[] handles = new WaitHandle[] { yourEvent, shutdownEvent };
if (WaitHandle.WaitAny(handles) == 1)
{
// Add code to end gracefully here.
}
// Your event was signaled so now we can proceed.
}
Вариант 3 - используйте Thread.Interrupt
Не путайте это с помощью Thread.Abort
. Прерывание потока определенно небезопасно, но прерывание потока совершенно другое. Thread.Interrupt
будет "вызывать" встроенные блокирующие вызовы, используемые в BCL, включая Thread.Join
, WaitHandle.WaitOne
, Thread.Sleep
и т.д.
Ответ 2
Ваша процедура, скорее всего, будет работать большую часть времени, но я не думаю, что есть какая-либо гарантия того, что один из ожидающих потоков будет reset событием между временем, установленным вашим циклом выключения, и временем, в течение которого цикл выключения снова проверяет его.
Я нахожу, что классы AutoResetEvent и ManualResetEvent отлично работают для действительно простых сценариев. В любое время, когда есть что-то странное в отношении требований, я быстро переключаюсь на более гибкий шаблон Wait And Pulse.
Если вы не нуждаетесь в какой-либо очистке, вы можете сделать свои потоки рабочего потока потоками, а затем они просто остановятся, когда основной поток выйдет.
Вы также можете определить второй метод ManualResetEvent, называемый stopRequest, и ждать сигнала от любого события. Однако это может не поддерживаться на компактной основе.
Ответ 3
Работает ли thread abort для вашей структуры?