Implement a Time-out in the waitQueue of the ThreadPool

G

Guest

We have a requirement to implement multithreaded processing for a function.
ThreadPool is used to implement multithreading in order to avoid the over
head of creating and destroying Threads dynamically.
A function will Queues the method calls to the default ThreadPool. If the
number of method calls is more than the ThreadPool size, it will get queued
for getting a slot in the ThreadPool.
Our requirement is to implement a Time-out in the waitQueue of the
ThreadPool. If a request is queued in the ThreadPool for a pre-defined amount
of time, it should get Timeout, notify the user and that particular request
should not go inside the ThreadPool again. In short, if a thread is not
available, the thread manager should wait for one to become available. If
none become available within a time period, then the process would fail out,
leaving the existing threads to complete

For implementing this we used ThreadPool.RegisterWaitForSingleObject method.
The problem here is that even though we can specify a Time out in
millisecond, the Timeout delegate also will get queued in the ThreadPool and
the Original request will get executed before the Timeout delegate. We have
to give some kind of priority to the Timeout delegate, so that if a request
gets Timeout, the same request will not get processed.

Sample Code which simulates the situation:

private void btnStart_Click(object sender, EventArgs e)
{
AutoResetEvent arEvent = new AutoResetEvent(false);
ThreadPool.SetMaxThreads(2, 2);
for (int i = 0; i < 10; i++)
{
ThreadPool.RegisterWaitForSingleObject(arEvent, new
WaitOrTimerCallback(Print), “Objectâ€+ i , 2000, true);
arEvent.Set();
//Time out is set as 2 seconds and ThreadPool size is 2
}
}



Public void Print(object state, bool timedOut)
{
TimeSpan waitTime = DateTime.Now.Subtract(tInfo.TaskQueuedTime);
if (waitTime.TotalMilliseconds < TIMEOUT)
{
MessageBox.Show("Executing " + state.ToString());
Thread.Sleep(3000);
//Thread is taking more than 3 seconds to complete the task
}
else
{
Console.out.WriteLine("Time Out " + state.ToString());
}
}

Based on our requrement, the ThreadPool should show MessageBox only for
first two items in the queue and it should write the Time Out in console for
all the remaining eight items in the queue. But its actual behaviour is that,
Its showing MessageBox for all the ten items in the Queue and shows Time Out
for the last eight items. The Time Out for last eight items are invoked after
showing all the ten MessageBoxes.

The reason why its happened is because, even the TimeOut delegate is also
getting queued in the ThreadPool.

Can anyone please suggest a solution to resolve this issue.
Thanking you in advance
 
W

William Stacey [MVP]

This is why the thread pool should not be used for long running tasks. The
long running tasks can fill the thread pool and not allow things like timer
callbacks to run - causing blocking issues. You can't even use one of the
System timers because the callback happens on the TP - which I presume could
be filled processing long running print jobs. One solution here is use your
own thread pool or just a couple worker threads and blocking queue. Your
state object could have a Cancel property on it, so you can cancel the task
after some timeout if it has not run yet. When a worker thread gets to the
work item, it will see it is cancelled and just move on to the next item.

--
William Stacey [MVP]

| We have a requirement to implement multithreaded processing for a
function.
| ThreadPool is used to implement multithreading in order to avoid the over
| head of creating and destroying Threads dynamically.
| A function will Queues the method calls to the default ThreadPool. If the
| number of method calls is more than the ThreadPool size, it will get
queued
| for getting a slot in the ThreadPool.
| Our requirement is to implement a Time-out in the waitQueue of the
| ThreadPool. If a request is queued in the ThreadPool for a pre-defined
amount
| of time, it should get Timeout, notify the user and that particular
request
| should not go inside the ThreadPool again. In short, if a thread is not
| available, the thread manager should wait for one to become available. If
| none become available within a time period, then the process would fail
out,
| leaving the existing threads to complete
|
| For implementing this we used ThreadPool.RegisterWaitForSingleObject
method.
| The problem here is that even though we can specify a Time out in
| millisecond, the Timeout delegate also will get queued in the ThreadPool
and
| the Original request will get executed before the Timeout delegate. We
have
| to give some kind of priority to the Timeout delegate, so that if a
request
| gets Timeout, the same request will not get processed.
|
| Sample Code which simulates the situation:
|
| private void btnStart_Click(object sender, EventArgs e)
| {
| AutoResetEvent arEvent = new AutoResetEvent(false);
| ThreadPool.SetMaxThreads(2, 2);
| for (int i = 0; i < 10; i++)
| {
| ThreadPool.RegisterWaitForSingleObject(arEvent, new
| WaitOrTimerCallback(Print), "Object"+ i , 2000, true);
| arEvent.Set();
| //Time out is set as 2 seconds and ThreadPool size is 2
| }
| }
|
|
|
| Public void Print(object state, bool timedOut)
| {
| TimeSpan waitTime = DateTime.Now.Subtract(tInfo.TaskQueuedTime);
| if (waitTime.TotalMilliseconds < TIMEOUT)
| {
| MessageBox.Show("Executing " + state.ToString());
| Thread.Sleep(3000);
| //Thread is taking more than 3 seconds to complete the task
| }
| else
| {
| Console.out.WriteLine("Time Out " + state.ToString());
| }
| }
|
| Based on our requrement, the ThreadPool should show MessageBox only for
| first two items in the queue and it should write the Time Out in console
for
| all the remaining eight items in the queue. But its actual behaviour is
that,
| Its showing MessageBox for all the ten items in the Queue and shows Time
Out
| for the last eight items. The Time Out for last eight items are invoked
after
| showing all the ten MessageBoxes.
|
| The reason why its happened is because, even the TimeOut delegate is also
| getting queued in the ThreadPool.
|
| Can anyone please suggest a solution to resolve this issue.
| Thanking you in advance
|
|
|
 
C

Chris Mullins

[Thread Pool is full of blocking events, and things are now broken]
Can anyone please suggest a solution to resolve this issue.
Thanking you in advance

You can't use the ThreadPool for long running operations, or for operations
that have the potential to block. As soon as you do this, things can fall
apart, because you and the .Net framework are sharing the threadpool, and
both of you rely on having available threads to use.

The long and short of it is that you have two real options:
1 - Make all of your thread pool operations 100% async
2 - Don't use the system thread pool.

I wrote up a detailed blog entry on this a few months back that can be found
at: http://www.coversant.net/dotnetnuke/Default.aspx?tabid=88&EntryID=8

As an aside, Jeff Richter has put together a "Power Threading Library" you
should take a look at. He's got a lock type in there that's pretty close to
what I think you're looking for. You can find his library at:
http://www.wintellect.com/ (look for the "Power Threading" section in for
"For Developers" list on the right hand side.
 
W

William Stacey [MVP]

I'll second the CCR. Is a great library. Having been doing much with it
lately. However not having solid redist plans for the CCR right now is
kinda scary.

--
William Stacey [MVP]

| Chris,
|
| Good blog. Have you seen what's available in the Currency and
| Coordination Runtime (CCR) developed by Microsoft? You said you wanted
| the ability to create different instances of a thread pool. That's
| built into the CCR using the Dispatcher class.
|
| http://channel9.msdn.com/wiki/default.aspx/Channel9.ConcurrencyRuntime
|
http://msdn.microsoft.com/msdnmag/issues/06/09/concurrentaffairs/default.aspx
|
| Brian
|
| Chris Mullins wrote:
| > I wrote up a detailed blog entry on this a few months back that can be
found
| > at: http://www.coversant.net/dotnetnuke/Default.aspx?tabid=88&EntryID=8
|
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top