![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 | |
|
Professional Programmer
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3
![]() |
C# detecting when a thread has finished
Can anybody tell whether it's possible to detect when a thread has finished and if so how?
I thought one way to do it would be to continously poll it's state using a timer or something similar. Is there a way to set an event to be triggered when the thread has finished.
__________________
Quote:
|
|
|
|
|
|
|
#2 |
|
Hobbyist Programmer
|
There is not an event natively exposed to us by the Framework libraries, however, as you mention, we can check the ThreadState, or similarly the IsAlive properties, though, another solution is to create your own event which the execution process of a thread can raise in order to return valuable data.
__________________
Mona Lisa must of had the highway blues you can tell by the way she smiles.. |
|
|
|
|
|
#3 |
|
Troll
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4
![]() |
A timer is one way to do it, but very wasteful.
Look at how the different timer classes operate. A Windows Forms timer (the one you can drag on to a form with the visual designer) operates by asking the OS to send a timer message at fixed intervals. This means that when the UI thread gets back to the message loop, your timer will go off at (approximately) the right time, in the same way that your app is notified of a mouse press or resize. It's not very accurate but it has one very important property -- it only involves the main thread. Attempting to call WinForms methods on any other thread will throw an exception. The two other timers are designed to spawn threads at your specified interval. As this Mono example shows, this may be implemented by having a background thread sleep for the given duration and then starting yet another one to run your code. That's a lot of extra threads to babysit another one. As you can see use of timers may be a bad idea for what you're doing. But the way in which they operate may tell you something. Is this a WinForms project? If so, you could use the Control.Invoke method right before your thread exits. This will cause the specified delegate to be called on the main thread. It is almost like using a WinForms timer in that the main thread will be signaled via the message loop, but only when the thread you are worried about is actually finished. If that is what you need, you may as well use the BackgroundWorker class. It handles that for you (See the RunWorkerCompleted event) in a much cleaner way. If you are not worried about a UI thread (maybe you want another worker thread to be notified, or have no UI), there's several options. You can use the Join method to block the current thread until either the target thread exits or a timeout is reached. This may not be desirable if you need to continue with other tasks, but is very handy for something like a "Stop" method when wrapping a background task in to its own class. You could also create an AutoResetEvent -- signal it when the thread is done, and wait on it elsewhere. This has the advantage that you can use the WaitHandle.WaitAny or WaitAll methods in addition (timeout optional), which is fantastic if you have multiple threads for which you need to be notified. A ManualResetEvent was used in the source file I linked to communicate with the worker thread. Please be very careful about misuse of timers. Keep your threads sleeping until they should be. The OS can usually do a very good job of waking them up when the time comes, assuming you use the right methods. Take a look at PowerTOP and the recent 'tickless idle' feature added to the Linux kernel. It's not just about making your app slower, you may also end up waking up the CPU and causing wasteful context switches. A timer in the wrong place might get the job done, but your users will be paying in cycles and battery power. Perhaps you could elaborate on what exactly you are trying to accomplish with multithreading, and I could make some suggestions.
__________________
MD5(sig) = bcef75433db02e9ad9bf81d6f7c5c270 Last edited by Dameon; Jul 31st, 2007 at 8:22 PM. |
|
|
|
|
|
#4 | |
|
Professional Programmer
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3
![]() |
wow, great responses.
Thanks for exposing the dirty works of timers Dameon, they were one of my options but i don't think i will take them any further. At the moment i am not worried about UI threads, i am just trying to do a base class which can do the following, Afterwards i might just use inheritance to derive a class from this base class which can interact with the UI. What i am trying todo is have a Queue of files to download and have a thread in the background just taking items from the Queue and just downloading files one by one. I could do all this two threads (main and download thread) by recursively calling the thread function when a download has finished:
downloadMethod(File to download)
{
while(more data)
read data from remotefile
store data into localfile
end
if(more items in the Queue)
downloadMethod(Next file in the Queue)
endif
}I will look into AutoResetEvent.
__________________
Quote:
|
|
|
|
|
|
|
#5 |
|
Professional Programmer
|
If the files to download aren't very big - and the download would be rather fast, you could use the threadpool. -> look into .net threadpool. It has a queue and you just add items and as soon as a threadpool thread is available the download will be processed.
Be careful not to use some async methods in the code for the download , the async methods also use the threadpool and you could go into a dead-lock.
__________________
Don't take life too seriously, it's not permanent ! |
|
|
|
|
|
#6 |
|
Troll
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4
![]() |
I assume you mean that files will be added to the queue from the main thread.
What did you intend to use a timer for? And why recursion instead of a loop? I should first point out that 'Next file in the queue' isn't that simple -- if you intend to use a Queue object to pass your tasks to your worker thread(s), you will need to lock it. As documentation states, a Queue will instantly explode if you try to mess with it in two places at once. This actually goes for most classes;if they are not specifically designated as being thread-safe, you have to be careful. If you've done something along those lines before without problems, that just means that you got lucky, and eventually things will break at the wrong time. Exploding queues are a great parlor trick, though. Using a queue was a good idea though. The problem is that you aren't aware of the facilities available to you. It's very possible to have a small number of threads safely take files to download from a queue, and when empty they will simply go to sleep until more work arrives. You can do this with auto-reset event (perfect example here), but using Monitor would be cleaner. I suggest you read a bit more than those small sections, however, to understand what's actually going on. There's a lot of background, and understanding is more important than this one solution. I'm linking to that guy's book a lot these days, aren't I? I should get commission.
__________________
MD5(sig) = bcef75433db02e9ad9bf81d6f7c5c270 |
|
|
|
|
|
#7 | |
|
Professional Programmer
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3
![]() |
>I assume you mean that files will be added to the queue from the main thread.
Not exactly, I was thinking of letting the class have a method which will add the method, which would be called with a struct that contains the file information (local path, remote path) The timer an recursion were just first thing that came to mind, I’ve scrapped both of them and opted for the following: The method that downloads files Begin downloadMethod
Infinite loop
Lock queue
If more items in queue
Item = dequeue item
Else
autoRes.waitOne
Endif
Unlock queue
While (more data)
Read data from remote file
Put data in local file
End
End
EndThe method which will add items to the que Begin AddItem
Lock queue
Enqueue new item
Unlock queue
If downloadThread equals waitSleepJoin
autoRes.Set
Endif
End
__________________
Quote:
|
|
|
|
|
|
|
#8 |
|
Troll
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4
![]() |
You don't want to wait on the AutoResetEvent while you have the queue locked. In your code the event will not be set until after the add method gets a lock. Until the event is set, it can't...
What you are looking for is Monitor.Wait and Monitor.Pulse. You can lock the queue, check if it's empty, and if so, call Wait. Wait will then release the lock. When adding an item, you will lock the queue, enqueue the item, pulse, and finally release the lock. You don't check the thread's state -- AddItem doesn't need to know which threads are working on the queue, it just needs to let someone waiting on the queue know that it should wake up. It's perfectly fine to pulse when all of your threads are busy. The pulse will do nothing. When one of the threads finishes its work item, it will return to the top of the loop, lock the queue, see that it isn't empty, and dequeue another item.
__________________
MD5(sig) = bcef75433db02e9ad9bf81d6f7c5c270 |
|
|
|
|
|
#9 | |
|
Professional Programmer
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3
![]() |
Ok the below should work as explained then. I tried it out and it works as required. Please tell me if i am doing anything wrong as i am fairly new to threads.
The method that downloads files: Begin downloadMethod
Infinite loop
Lock queue
If queue is empty
Monitor.Wait
Else
download equals true
item equals next item in the queue
Endif
Unlock queue
While (more data and download equals true)
Read data from remote file
Put data in local file
End
End
EndThe method which will add items to the que Begin AddItem
Lock queue
Enqueue new item
Monitor.PulseAll
Unlock queue
End
__________________
Quote:
|
|
|
|
|
|
|
#10 |
|
Troll
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4
![]() |
PulseAll will wake up all waiting threads, not just one. It's useful for signaling all waiting threads to check an exit condition, for example, but here one work item needs a maximum of one worker woken up (a worker that just finished could still get the lock first).
It looks as though it should work, but it wastes a perfectly good lock on the queue and is difficult to follow. If the queue is empty, it will Wait as it should, but when it gets the lock back, it does not dequeue a work item. It instead unlocks the queue, skips the download loop, and tries to get the lock back at the top of the loop. More typically one would write: While (queue is empty and not exiting) Monitor.Wait if (exiting) exit //If it reaches here, there's something in the queue item equals next item in queue Much more straightforward. Also note the addition of an exit condition. Rather than shooting your thread in the face when you want it to exit, an additional meaning of Pulse is added. Elsewhere you would have a 'Stop' method, for example, that would lock the queue, set exiting to true, and then PulseAll. All the worker threads would then exit either immediately after waking up or after finishing their current work items. In my example, the threads would still exit even if there are work items left in the queue, but if that behavior is not desirable then only minor changes are required. Make sure 'exiting' is declared 'volatile'. This is one of a few specific cases where it is acceptable to not lock a variable shared between threads, but it must be marked as volatile to ensure that all threads see the most recent version of it.
__________________
MD5(sig) = bcef75433db02e9ad9bf81d6f7c5c270 |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| How to get current thread ID? | myName | C++ | 2 | Jul 6th, 2006 1:52 AM |
| threads | jayme | C++ | 21 | Jan 28th, 2006 10:20 PM |
| Finding the posts by a particular user in a particular thread. | InfoGeek | Community Announcements and Feedback | 9 | Nov 4th, 2005 2:43 AM |
| CPU Usage goes to 100% when pthread_cond_wait is being used | zen_buddha | C++ | 1 | Oct 13th, 2005 5:59 AM |
| Passing array as argument to a thread | Symptom | C | 5 | Sep 30th, 2005 6:52 PM |