Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Jul 31st, 2007, 4:27 PM   #1
kruptof
Professional Programmer
 
kruptof's Avatar
 
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3 kruptof is on a distinguished road
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:
When I was young it seemed that life was so wonderful,a miracle, oh it was beautiful, magical.
Now watch what you say or they'll be calling you a radical,a liberal, oh fanatical, criminal. Oh won't you sign up your name,we'd like to feel you're acceptable, respectable, oh presentable, a vegetable
kruptof is offline   Reply With Quote
Old Jul 31st, 2007, 7:28 PM   #2
john Wesley
Hobbyist Programmer
 
john Wesley's Avatar
 
Join Date: May 2006
Location: United Kingdom
Posts: 119
Rep Power: 3 john Wesley is on a distinguished road
Send a message via MSN to john Wesley Send a message via Yahoo to john Wesley
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..
john Wesley is offline   Reply With Quote
Old Jul 31st, 2007, 8:05 PM   #3
Dameon
Troll
 
Dameon's Avatar
 
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4 Dameon is on a distinguished road
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.
Dameon is offline   Reply With Quote
Old Aug 1st, 2007, 6:08 AM   #4
kruptof
Professional Programmer
 
kruptof's Avatar
 
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3 kruptof is on a distinguished road
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
}
but if i have alot of items in the Queue this might might exhaust the stack, so i can't do that.

I will look into AutoResetEvent.
__________________
Quote:
When I was young it seemed that life was so wonderful,a miracle, oh it was beautiful, magical.
Now watch what you say or they'll be calling you a radical,a liberal, oh fanatical, criminal. Oh won't you sign up your name,we'd like to feel you're acceptable, respectable, oh presentable, a vegetable
kruptof is offline   Reply With Quote
Old Aug 1st, 2007, 8:18 AM   #5
xavier
Professional Programmer
 
xavier's Avatar
 
Join Date: Oct 2004
Location: .ro
Posts: 383
Rep Power: 4 xavier is on a distinguished road
Send a message via Yahoo to xavier
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 !
xavier is offline   Reply With Quote
Old Aug 1st, 2007, 8:59 AM   #6
Dameon
Troll
 
Dameon's Avatar
 
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4 Dameon is on a distinguished road
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
Dameon is offline   Reply With Quote
Old Aug 1st, 2007, 3:41 PM   #7
kruptof
Professional Programmer
 
kruptof's Avatar
 
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3 kruptof is on a distinguished road
>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
End

The 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:
When I was young it seemed that life was so wonderful,a miracle, oh it was beautiful, magical.
Now watch what you say or they'll be calling you a radical,a liberal, oh fanatical, criminal. Oh won't you sign up your name,we'd like to feel you're acceptable, respectable, oh presentable, a vegetable
kruptof is offline   Reply With Quote
Old Aug 1st, 2007, 4:13 PM   #8
Dameon
Troll
 
Dameon's Avatar
 
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4 Dameon is on a distinguished road
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
Dameon is offline   Reply With Quote
Old Aug 2nd, 2007, 12:23 PM   #9
kruptof
Professional Programmer
 
kruptof's Avatar
 
Join Date: May 2006
Location: UK - London
Posts: 330
Rep Power: 3 kruptof is on a distinguished road
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
End

The method which will add items to the que
Begin AddItem
  Lock queue
    Enqueue new item
    Monitor.PulseAll
  Unlock queue
End
__________________
Quote:
When I was young it seemed that life was so wonderful,a miracle, oh it was beautiful, magical.
Now watch what you say or they'll be calling you a radical,a liberal, oh fanatical, criminal. Oh won't you sign up your name,we'd like to feel you're acceptable, respectable, oh presentable, a vegetable
kruptof is offline   Reply With Quote
Old Aug 2nd, 2007, 3:51 PM   #10
Dameon
Troll
 
Dameon's Avatar
 
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4 Dameon is on a distinguished road
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
Dameon is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

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




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 11:00 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC