473,326 Members | 2,655 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,326 software developers and data experts.

Asynchronous Delegates always using same worker thread

I've been running many tests of the Asynchronous Delegate technique
and I find that although BeginInvoke() does queue the method delegate
onto a worker thread, it always does so on the _same_ thread. So if I
call BeginInvoke() three times in a row, the method delegates are
queued to the same thread and the second method doesn't begin until
the first method completes, and the third doesn't begin until the
second completes.

This occurs whether I detect completion through callback,
AsyncWaitHandle, or polling. It occurs no matter whether the delegate
refers to the same instance method or different instance methods.

The bottom line is that I don't get multi-threaded behavior, but only
two-threaded behavior.

I believe this is not the intended behavior. Has anyone else seen
this? Does anyone know why this is happening?

--
Dave Hein
Nov 16 '05 #1
4 2174
Not sure what you are exactly doing in your method, but this is the normal
behavior when:
1 - the method returns before it consumed it's CPU quota (this without
executing a blocking call) AND
2 - you run this on a single CPU machine.

Willy.
"Coot" <dh***@acm.org> wrote in message
news:a3**************************@posting.google.c om...
I've been running many tests of the Asynchronous Delegate technique
and I find that although BeginInvoke() does queue the method delegate
onto a worker thread, it always does so on the _same_ thread. So if I
call BeginInvoke() three times in a row, the method delegates are
queued to the same thread and the second method doesn't begin until
the first method completes, and the third doesn't begin until the
second completes.

This occurs whether I detect completion through callback,
AsyncWaitHandle, or polling. It occurs no matter whether the delegate
refers to the same instance method or different instance methods.

The bottom line is that I don't get multi-threaded behavior, but only
two-threaded behavior.

I believe this is not the intended behavior. Has anyone else seen
this? Does anyone know why this is happening?

--
Dave Hein

Nov 16 '05 #2
"Willy Denoyette [MVP]" <wi*************@pandora.be> wrote in message news:<eg**************@TK2MSFTNGP12.phx.gbl>...
Not sure what you are exactly doing in your method, but this is the normal
behavior when:
1 - the method returns before it consumed it's CPU quota (this without
executing a blocking call) AND
2 - you run this on a single CPU machine.

Willy.


Yes, it is a single CPU machine _BUT_ no the method doesn't return
that quickly.

Here is the method:

public int TestMethod( TimeSpan callDuration, out int threadId )
{
threadId = AppDomain.GetCurrentThreadId();

int myThreadId = threadId;
int count = 10;
TimeSpan subDuration = TimeSpan.FromTicks( callDuration.Ticks /
count );
Type t = this.GetType();
for (int i = 0; i < count; ++i)
{
Thread.Sleep( subDuration );
Console.WriteLine(
"[" + myThreadId.ToString() + "] "
+ t.FullName + ".TestMethod(): " + DateTime.Now.TimeOfDay + " "
+ i.ToString() );
}

return threadId;
}
I'm invoking it with call durations of 0.5 to 1.0 seconds.

The problem occurs if I create a delegate for this method and then use
the BeginInvoke() method of the delegate.

Note: I've also created another class that provides a threaded
implementation that calls that same method via a custom
BeginTestMethod() (and returns results through a custom
EndTestMethod()) -- when I create thread in my custom method I can get
a large number of these TestMethods() executing concurrently -- on my
single CPU box.

But using the async delegate and BeginInvoke(), all I can do is queue
multiple invocations, but they all end up executing, in sequence, on
the same worker thread. :-(

--
Dave Hein

[snip]
Nov 16 '05 #3

"Coot" <dh***@acm.org> wrote in message
news:a3**************************@posting.google.c om...
"Willy Denoyette [MVP]" <wi*************@pandora.be> wrote in message
news:<eg**************@TK2MSFTNGP12.phx.gbl>...
Not sure what you are exactly doing in your method, but this is the
normal
behavior when:
1 - the method returns before it consumed it's CPU quota (this without
executing a blocking call) AND
2 - you run this on a single CPU machine.

Willy.


But using the async delegate and BeginInvoke(), all I can do is queue
multiple invocations, but they all end up executing, in sequence, on
the same worker thread. :-(

--
Dave Hein

[snip]

Well, this code:

using System;
using System.Threading;
namespace Willys
{
class Tester
{
static void Main()
{
Target t = new Target();
t.SpawnEm();
}
}
}

public class Target {
public delegate int LaunchProc(TimeSpan ts, out int v);
public int TestMethod( TimeSpan callDuration, out int threadId )
{
threadId = AppDomain.GetCurrentThreadId();
int myThreadId = threadId;
int count = 10;
TimeSpan subDuration = TimeSpan.FromTicks( callDuration.Ticks /count );
Type t = this.GetType();
for (int i = 0; i < count; ++i)
{
Thread.Sleep( subDuration );
Console.WriteLine("[" + myThreadId.ToString() + "] "
+ t.FullName + ".TestMethod(): " + DateTime.Now.TimeOfDay + " "
+ i.ToString() );
}
return threadId;
}
public void SpawnEm() {
int count = 2;
IAsyncResult[] asr = new IAsyncResult[count];
int threadId = 0;
LaunchProc pfn = new LaunchProc(this.TestMethod);
for (int t = 0; t< count; t++) {
asr[t] = pfn.BeginInvoke(new TimeSpan(20000000),out threadId, null,
null);
}
foreach(IAsyncResult ar in asr)
{
pfn.EndInvoke(out threadId, ar);
Console.WriteLine("Thread [{0}] Done", threadId);
}
}
}

Gives this:
[2856] Target.TestMethod(): 19:03:39.0448656 0
[2856] Target.TestMethod(): 19:03:39.2451536 1
[2856] Target.TestMethod(): 19:03:39.4454416 2
[2976] Target.TestMethod(): 19:03:39.5455856 0
[2856] Target.TestMethod(): 19:03:39.6457296 3
[2976] Target.TestMethod(): 19:03:39.7458736 1
[2856] Target.TestMethod(): 19:03:39.8460176 4
[2976] Target.TestMethod(): 19:03:39.9461616 2
[2856] Target.TestMethod(): 19:03:40.0463056 5
[2976] Target.TestMethod(): 19:03:40.1464496 3
[2856] Target.TestMethod(): 19:03:40.2465936 6
[2976] Target.TestMethod(): 19:03:40.3467376 4
[2856] Target.TestMethod(): 19:03:40.4468816 7
[2976] Target.TestMethod(): 19:03:40.5470256 5
[2856] Target.TestMethod(): 19:03:40.6471696 8
[2976] Target.TestMethod(): 19:03:40.7473136 6
[2856] Target.TestMethod(): 19:03:40.8474576 9
Thread [2856] Done
[2976] Target.TestMethod(): 19:03:40.9476016 7
[2976] Target.TestMethod(): 19:03:41.1478896 8
[2976] Target.TestMethod(): 19:03:41.3481776 9
Thread [2976] Done

So you see clearly that two threadpool threads are activated.

Willy.
Nov 16 '05 #4
Willy,

The behavior seems to be related to how quickly the threads execute.

I modified your code, which uses a 2-second thread duration, to make 3
calls instead of two. All three were executed on a different thread.

However, I then changed it to a 0.2-second thread duration. This time
all 3 calls executed in sequence on the same thread.

And finally, I hunted for the sweet spot and found that with a 0.5
thread duration, the first and second calls executed on different
threads, but the 3rd executed on the same thread as the first.

Not sure why there is some delay in scheduling onto a new thread, but
it seems that the first request is executed immediately, the second is
executed after waiting a 2 or 3 tenths of a second, and the 3rd is
executed after waiting 5 tenthds of a second. Since all were
dispatched within microseconds of each other, I don't understand why
the delay in initiating their execution.

Perhaps this is a tunable policy of the thread pool manager???

--
Dave Hein

"Willy Denoyette [MVP]" <wi*************@pandora.be> wrote in message news:<u8**************@TK2MSFTNGP12.phx.gbl>...
[snip]
Well, this code:

using System;
using System.Threading;
namespace Willys
{
class Tester
{
static void Main()
{
Target t = new Target();
t.SpawnEm();
}
}
}

public class Target {
public delegate int LaunchProc(TimeSpan ts, out int v);
public int TestMethod( TimeSpan callDuration, out int threadId )
{
threadId = AppDomain.GetCurrentThreadId();
int myThreadId = threadId;
int count = 10;
TimeSpan subDuration = TimeSpan.FromTicks( callDuration.Ticks /count );
Type t = this.GetType();
for (int i = 0; i < count; ++i)
{
Thread.Sleep( subDuration );
Console.WriteLine("[" + myThreadId.ToString() + "] "
+ t.FullName + ".TestMethod(): " + DateTime.Now.TimeOfDay + " "
+ i.ToString() );
}
return threadId;
}
public void SpawnEm() {
int count = 2;
IAsyncResult[] asr = new IAsyncResult[count];
int threadId = 0;
LaunchProc pfn = new LaunchProc(this.TestMethod);
for (int t = 0; t< count; t++) {
asr[t] = pfn.BeginInvoke(new TimeSpan(20000000),out threadId, null,
null);
}
foreach(IAsyncResult ar in asr)
{
pfn.EndInvoke(out threadId, ar);
Console.WriteLine("Thread [{0}] Done", threadId);
}
}
}

Gives this: [snip] Thread [2856] Done [snip] Thread [2976] Done

So you see clearly that two threadpool threads are activated.

Willy.

Nov 16 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

12
by: Joey Powell | last post by:
Re: Original post = Windows forms - how do I get them to render/update properly? from August 22. Okay I am making some progress with being able to use delegates to run my shelled processes on...
4
by: Matthew Groch | last post by:
Hi all, I've got a server that handles a relatively high number of concurrent transactions (on the magnitude of 1000's per second). Client applications establish socket connections with the...
1
by: Natalia DeBow | last post by:
Hi, I am working on a Windows-based client-server application. I am involved in the development of the remote client modules. I am using asynchronous delegates to obtain information from...
3
by: Oscar Thornell | last post by:
Hi, I am thinking about doing all my logging asynchronously using a delegate. The main resaon for this would be performance and responsiveness of the application (an ASP.NET app). //Exampel...
7
by: brisers | last post by:
Hello, After studying some of the available resouces ( http://msdn.microsoft.com/msdnmag/issues/04/05/BasicInstincts/default.aspx) I can see that it is forbidden to modify the UI directly from...
3
by: Morgan Cheng | last post by:
Asynchronous I/O is said to be using native completion port. However, i checked rotor2.0 code, it seems not in stream.cs. BeginRead is defined in below way. In my understanding, If BeginInvoke is...
4
by: Morgan Cheng | last post by:
Since ASP.NET 2.0, asynchronous web service client can be implemented with event-based pattern, instead of original BeginXXX/EndXXX pattern. However, I didn't find any material about event-based...
3
by: =?Utf-8?B?Sm9obg==?= | last post by:
Hi, I need to write asynchronous HTTPModule which will sometimes execute long job. I've written it using AddOnBeginRequestAsync but it still executes synchronously - I am checking performance...
0
by: alan | last post by:
Hello all, I'd like to ask recommendations about a "good" generic asynchronous I/O library for C++. By generic I mean, something I can use even on files, stdin/stdout, and sockets. I've seen...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.