Завершить поток по истечении интервала, если не был возвращен

У меня есть поток, который собирает данные из сети или последовательного порта. Поток должен завершиться (или вернуть false), если данные не получены в течение 5 секунд.

Другими словами, если выполнение потока занимает более 5 секунд, он должен остановиться.

Я пишу на C #, но подойдет любой язык .NET.


person Mahdi    schedule 21.12.2010    source источник
comment
Лучшие ответы на stackoverflow.com/questions/299198/implement-c-generic -timeout   -  person Simon Munro    schedule 29.03.2011


Ответы (6)


Есть два подхода:

1. Инкапсулированный тайм-аут

Поток, считывающий данные из сети или последовательного порта, может измерять время, прошедшее с момента его запуска, и ждать данных не более, чем оставшееся время. API сетевого взаимодействия обычно предоставляют средства для указания тайм-аута для операции. Следовательно, выполняя простые DateTime арифметические операции, вы можете инкапсулировать управление тайм-аутом в своем рабочем потоке.

2. Внешний тайм-аут

Используйте другой поток (или сделайте это в основном потоке, если это возможно), чтобы дождаться завершения рабочего потока в течение определенного срока, а если нет, прервите его. Нравится:

// start the worker thread
...

// give it no more than 5 seconds to execute
if (!workerThread.Join(new TimeSpan(0, 0, 5)))
{    
    workerThread.Abort();
}

Рекомендация. Я бы остановился на первом решении, так как оно приводит к созданию более чистого и удобного в обслуживании дизайна. Однако в определенной ситуации может потребоваться предоставить средства для «жесткого» прерывания таких рабочих потоков.

person Ondrej Tucny    schedule 21.12.2010
comment
Как я уже упоминал в других ответах, Thread.Abort() устарел, и он даже не прерывает потоки, заблокированные в неуправляемом коде. - person cdhowie; 21.12.2010
comment
@cdhowie Рабочий поток не обязательно должен блокироваться на неопределенное время в одном неуправляемом вызове API. Он может выполнять более сложную обработку получаемых данных, и, следовательно, «Thread.Abort ()» может быть подходящим решением в некоторых случаях. Однако в целом вы правы. - person Ondrej Tucny; 21.12.2010
comment
Но Abort() оставляет процесс в неопределенном состоянии, поскольку поток мог быть прерван в середине критического раздела. Вот почему его никогда не следует использовать. - person cdhowie; 21.12.2010
comment
Не могли бы вы проверить, правильно ли это? [code] ThreadPool.QueueUserWorkItem ((obj = ›{Thread workerThread = new Thread (o =› {for (int i = 0; i ‹10000000; i ++) {}}); workerThread.Start (); if (! workerThread .Join (новый TimeSpan (0, 0, 5))) {workerThread.Abort (); MessageBox.Show (Aborted);}})); [/ code] - person Mahdi; 21.12.2010
comment
@Mahdi Образец кажется правильным. Но учтите, что пустое «for» будет выполняться так быстро, что вы не увидите сообщение «Прервано». Лучше использовать Thread.Sleep(...) для моделирования рабочей нагрузки и поиграть с алгоритмом. Кроме того, я не совсем понимаю комбинацию пула потоков и отдельных потоков. Однако правильный дизайн зависит от вашей конечной цели. - person Ondrej Tucny; 21.12.2010

Любой из этих механизмов должен позволять вам указывать тайм-аут при чтении. Например, вы можете установить для NetworkStream.ReadTimeout значение 5000 перед выполнением чтения. Вы должны иметь возможность сделать что-то подобное для последовательных портов.

При чтении с истекшим тайм-аутом будет возвращено 0 из Stream.Read() - так вы можете узнать, действительно ли произошло тайм-аут.

Обратите внимание, что использование Thread.Abort() устарело в .NET 2.0 из-за некоторых серьезных проблем, которые оно может вызвать. Теоретически вы можете использовать Thread.Interrupt(), но это ничего не даст, если поток заблокирован в неуправляемом коде (включая ввод-вывод). Использование тайм-аутов чтения - правильный способ решить эту проблему.

person cdhowie    schedule 21.12.2010
comment
Вы бросаете deprecated, но в статьях библиотеки MSDN (2.0 и 4.0) об этом ничего не говорится. Конечно, нет [Устарело]. Вам действительно следует задокументировать источник вашего заявления. - person Hans Passant; 21.12.2010
comment
@Hans: Сообщество более или менее устарело. Это сообщение в блоге довольно хорошо описывает рассуждения. Одна из худших вещей, которую может сделать Abort(), - это привести к тому, что полученная блокировка не будет должным образом освобождена потоком. - person cdhowie; 21.12.2010
comment
Ох, они, пытались послать им электронное письмо об этом, но они так и не ответили. Я изменил структуру назло. - person Hans Passant; 21.12.2010

Если вы используете управляемый код для блокировки потока, вы можете использовать Thread.Abort, чтобы прервать выполнение. Если вы используете сторонний код или COM-взаимодействие, остановить выполнение потока невозможно. Единственный способ - установить для Thread.isBackroundThread значение true, а затем завершить процесс ...

person alex    schedule 21.12.2010
comment
Я использую встроенную dll c ++ и DllImportAttribute. - person Mahdi; 21.12.2010
comment
Thread.Abort() устарел, начиная с .NET 2.0, и его использование считается плохой практикой. - person cdhowie; 21.12.2010
comment
Я знаю это и знаю обо всех проблемах, которые это может вызвать, поэтому я сказал, что если вы используете управляемый код, завершение цикла while (...) {} с помощью Thread.Abort вполне нормально. Никаких критических секций или около того ... - person alex; 21.12.2010

Вы упомянули, что читаете из «сети». Если вы используете NetworkStream для чтения, вы можете указать ReadTimeout равным 5000 (это в миллисекундах). Это будет ждать 5 секунд, а затем вернется после этого, если данные не были прочитаны. Однако, если он в процессе или чтении, я думаю, он может вернуться, так что будьте осторожны. Я никогда ничего не делал с последовательным API, поэтому я не уверен, есть ли там такая настройка.

person Merky    schedule 21.12.2010

Это двухпотоковое решение.

Пример:

Thread tParent = new Thread(new ThreadStart(() =>
{
    Thread t = new Thread(AsyncMethod);
    t.Start();
    Thread.Sleep(5000);
    if (t.IsAlive) t.Abort();
}));

Помните:

Использовать такой метод - плохая практика. Вызов Thread.Abort() не рекомендуется. Вместо этого используйте Thread Synchronization, используя WaitHandles. Например, AutoResetEvent.

person decyclone    schedule 21.12.2010

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

Теперь, что касается завершения потока, см. Ответ Алексея, это правильно!

person VdesmedT    schedule 21.12.2010