Reputation: 31
I have a web service in which I need to grab a bunch of records and do some processing on each one. This processing can be lengthy per each row, and I am running this portion of the code in a threaded fashion, passing the row's data as a parameter into the function. I could have 1000 or more rows to process, and I limit the number of data processing threads to 10.
To monitor my threads, I have an array of Thread objects.
Dim RowThread(9) As Thread
On my main thread (a web service WebMethod) before I spawn a new thread I loop through this array and look for my next 'available' thread.
Dim avail_thread As Integer = -1
While avail_thread < 0
For t As Integer = 0 To THREADCOUNT - 1
If IsNothing(RowThread(t)) OrElse Not RowThread(t).IsAlive Then
avail_thread = t
Exit For
End If
Next
End While
...
RowThread(avail_thread) = New Thread(New ParameterizedThreadStart(AddressOf ProcessRow))
...
RowThread(avail_thread).Start(row)
As you can see, it waits until a thread is done and will spawn the next row on the next available thread. If all 10 threads are in use, it will continue to loop and wait until one because available.
This approach works great and runs well on most servers. Except recently on some servers I've noticed this causing problems because it pegs out the CPU while its in this loop. This affects the overall performance because my Main Thread is wasting a ton of CPU cycles. To ease this I've tried thread Sleep() and even DoEvents() but these approaches only further degrade performance.
Has anyone else come across this scenario? I don't really think I can use delegates, etc.. since this is within a web service, I don't want my main call to be ended because I don't want a result sent until all rows have been processed. Same issue with the ThreadPool, not to mention ThreadPool's do give you as much control.
How to I responsibly spawn and then manage threads from within a Web Service? Is there a way to perform non-blocking manual thread monitoring?
Upvotes: 0
Views: 470
Reputation: 31
I think I've found an answer in WaitHandle. In my previous research I thought it was only possible to use these with the ThreadPool but it turns out you can use them with manually started threads as well.
I now use:
RowEvents(avail_thread) = New ManualResetEvent(False)
then later pass this in as part of my parameter object on the thread Start. when that function is done I do the Set() on the ManualResetEvent object for that thread.
My find next available thread code now looks like this:
Dim avail_thread As Integer = -1
While avail_thread < 0
For t As Integer = 0 To THREADCOUNT - 1
If IsNothing(RowThread(t)) OrElse Not RowThread(t).IsAlive Then
avail_thread = t
Exit For
End If
Next
'All threads busy, call blocking wait
If avail_thread < 0 Then avail_thread = WaitHandle.WaitAny(RowEvents)
End While
This will now loop through all 10 threads on the first pass.. If they're all running then I call the WaitAny method, focus is returned when any of the threads complete and sets my avail_thread variable to the index thread that finished.
Upvotes: 0
Reputation: 171178
Why don't you just post all work to the thread-pool? It will do all of this for you in a perfectly correct and efficient way. It will create the right number of threads and distribute work items among them. When you use the TPL's Task class you can even monitor the tasks completion.
Upvotes: 2