How does CLR distinguish worker thread and IO thread?

M

Morgan Cheng

By default, ThreadPool has 25 worker threads per CPU and 1000 IO
threads for each process. I am wondering how CLR managed to allocate
IO asynchronous tasks to IO thread and other asynchonous tasks to
worker thread.

With the help of Reflector, I found that default implementation of
Stream.Read just use BeginInovke to get a IAsynResult.

ReadDelegate delegate2 = new ReadDelegate(this.Read);
... ...
return delegate2.BeginInvoke(buffer, offset, count, callback,
state);

Since subclass of Stream may not involves I/O, e.g. MemoryStream. This
is understandable.

According to reflected code of FileStream.BeginRead, it is indeed
implemented with I/O completion port. That is also fine.

However, for stream returned by HttpResponse.GetResponseStream is
System.Net.ConnectStream. And it use System.Net.Connection to do
BeginRead. Connection has no its own implementation. So, it is
actually use Stream.BeginRead. As a result, ConnnectStream.BeginRead
use BeginInvoke. In my understanding, task passed to BeginInvoke will
be dispatched to worker thread.

I make a small program to launch 50 Http requests and read their
response streams with BeginRead. There is no threads burst. It seems
that completion port i/o is still used. I set breakpoint at callback
to BeginRead, the stack trace shows that it is called from completion
port as below

System.dll!System.Net.LazyAsyncResult.Complete(System.IntPtr
userToken) + 0x7f bytes
System.dll!System.Net.ContextAwareResult.CompleteCallback(object
state) + 0x1a bytes
mscorlib.dll!
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext
executionContext, System.Threading.ContextCallback callback, object
state) + 0x81 bytes
System.dll!System.Net.ContextAwareResult.Complete(System.IntPtr
userToken) + 0xa7 bytes
System.dll!System.Net.LazyAsyncResult.ProtectedInvokeCallback(object
result, System.IntPtr userToken) + 0x8b bytes
System.dll!
System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(uint
errorCode, uint numBytes, System.Threading.NativeOverlapped*
nativeOverlapped) + 0x116 bytes
mscorlib.dll!
System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint
errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP)
+ 0x68 bytes

I cannot understand this. System.Net.ConnectStream is using
BeginInvoke to launch asynchronous task; and the task is done with IO
completion port. Then, CLR must have a way to distinguish IO task and
other task, but how?
 
J

Jon Skeet [C# MVP]

However, for stream returned by HttpResponse.GetResponseStream is
System.Net.ConnectStream. And it use System.Net.Connection to do
BeginRead. Connection has no its own implementation. So, it is
actually use Stream.BeginRead. As a result, ConnnectStream.BeginRead
use BeginInvoke. In my understanding, task passed to BeginInvoke will
be dispatched to worker thread.

I think your analysis is flawed. System.Net.Connection derives from
PooledStream. PooledStream.BeginRead uses NetworkStream.BeginRead,
which uses Socket.BeginReceive. I believe it's that which uses IO
completion ports.

Jon
 
M

Morgan Cheng

I think your analysis is flawed. System.Net.Connection derives from
PooledStream. PooledStream.BeginRead uses NetworkStream.BeginRead,
which uses Socket.BeginReceive. I believe it's that which uses IO
completion ports.

You are correct. I checked it again and System.Net.Connection does
derive from PolledStream.
Thanks for you correction.
 

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