14

I have a thread which grabs some data from network or serial port. The thread must terminate (or return false) if no data is received within 5 seconds.

In other words, if running the thread is taking more than 5 seconds it must stop.

I am writing in C#, but any .NET language is OK.

Andrii Kalytiiuk
  • 1,501
  • 14
  • 26
Mahdi
  • 198
  • 2
  • 2
  • 9

6 Answers6

9

There are two approaches:

1. Encapsulated timeout

The thread reading the data from network or serial port can measure time elapsed from its time of start and wait for the data for no more than the remaining time. Network communication APIs usually provide means to specify a timeout for the operation. Hence by doing simple DateTime arithmetic you can encapsulate timeout management within your worker thread.

2. External timeout

Use another thread (or do it in the main thread if that's feasible) to wait for the worker thread to finish within a certain time limit, and if not, abort it. Like this:

// start the worker thread
...

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

Recommendation: I'd stick with the first solution, as it leads to cleaner and maintainable design. However, in certain situation it might be necessary to provide means for 'hard' abort of such worker threads.

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
  • As I've mentioned on other answers, `Thread.Abort()` is deprecated, and it will not even abort threads blocked in unmanaged code. – cdhowie Dec 21 '10 at 17:12
  • 1
    @cdhowie The worker thread need not necessarily block indefinitely in a single unmanaged API call. It may be performing more complex processing of the data being received, and hence ´Thread.Abort()´ may be a valid solution in certain cases. However, generally, you are right. – Ondrej Tucny Dec 21 '10 at 17:17
  • But `Abort()` leaves the process in an indeterminate state, since the thread might have been aborted in the middle of a critical section. This is why it should never be used. – cdhowie Dec 21 '10 at 17:18
  • Would you please check if this is correct ? [code]ThreadPool.QueueUserWorkItem((obj => { Thread workerThread = new Thread(o => { for (int i = 0; i < 10000000; i++) { } }); workerThread.Start(); if (!workerThread.Join(new TimeSpan(0, 0, 5))) { workerThread.Abort(); MessageBox.Show("Aborted"); } }));[/code] – Mahdi Dec 21 '10 at 17:31
  • @Mahdi The sample seems correct. But note that the empty ´for´ will execute so fast you won't get the "Aborted" message shown. Better use a `Thread.Sleep(...)` to simulate workload and play with the algorithm. Also, I don't quite understand the combination of thread pool and separate threads. However, the correct design depends on your ultimate goal. – Ondrej Tucny Dec 21 '10 at 17:50
4

Either mechanism should allow you to specify a timeout on the read. For example, you could set NetworkStream.ReadTimeout to 5000 before performing the read. You should be able to do something similar for serial ports.

A read that has timed out will return 0 from Stream.Read() -- this is how you can tell if a timeout actually occurred.

Note that using Thread.Abort() is deprecated as of .NET 2.0 for some serious problems it can cause. You could theoretically use Thread.Interrupt(), but this will not do anything when the thread is blocked in unmanaged code (including I/O). Using read timeouts is the proper way to solve this problem.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • 2
    You are flinging `deprecated` but the MSDN Library articles (2.0 and 4.0) are completely silent about that. Certainly no [Obsolete]. You really should document the source of your statement. – Hans Passant Dec 21 '10 at 17:42
  • @Hans: It's more or less been deprecated by the community. [This blog post](http://msmvps.com/blogs/peterritchie/archive/2007/08/22/thead-abort-is-a-sign-of-a-poorly-designed-program.aspx) does a pretty good job covering the reasoning. One of the worst things `Abort()` can do is cause an acquired lock to not get properly released by the thread. – cdhowie Dec 21 '10 at 17:58
  • Oh them, tried to send them an email about it but they never responded. I mutated a struct out of spite. – Hans Passant Dec 21 '10 at 18:18
3

If you are using managed code to block the thread, you can use Thread.Abort to abort the execution. If you are using 3rd party code or COM interop, there is no way of terminating the thread execution. The only way is setting the Thread.isBackroundThread to true, and then terminating the Process...

alex
  • 1,228
  • 1
  • 16
  • 38
  • I am using a native c++ dll and DllImportAttribute. – Mahdi Dec 21 '10 at 17:07
  • 1
    `Thread.Abort()` is deprecated as of .NET 2.0 and it's considered bad practice to use it. – cdhowie Dec 21 '10 at 17:08
  • I know that and I'm aware of all problems it could possibly cause, threfore I said "If you are using managed code", cause terminating a while(...){ } loop with `Thread.Abort` is perfectly fine. No critical sections or so... – alex Dec 21 '10 at 17:30
2

You mentioned you are reading from a "network". If you are using NetworkStream to read you can specify the ReadTimeout to be 5000 (it is in milliseconds). That will wait for 5 seconds then return after that if no data has been read. However if it is in the process or reading I think it might return also so be careful. I've never done anything with the serial API so I'm not sure if there is a setting in there like that.

Merky
  • 494
  • 2
  • 4
2

This is a two thread solution.

Example:

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

Remember:

It is a bad practice to use a method like this. Calling Thread.Abort() is not recommended approach. Use Thread Synchronization using WaitHandles instead. For example, AutoResetEvent.

decyclone
  • 30,394
  • 6
  • 63
  • 80
1

I guess a watcher thread is what you need. Start another thread at the same time and passes the serial port thread as parameter of that thread. Start the watcher with a five second sleep and check the status of the thread.

Now as for terminating the thread, see alex's answer, its just right !

VdesmedT
  • 9,037
  • 3
  • 34
  • 50