APM behaviour, is this a bug?

  • Thread starter Alexander Malapheev
  • Start date
A

Alexander Malapheev

Hello,
I'm writing an application which use APM.

For example:

class Program
{
static Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
static void Main(string[] args)
{
IAsyncResult res = socket.BeginConnect("microsoft.com", 80,
null, null);
socket.EndConnect(res);
Console.ReadLine();
}
}

In this example calling of socket.BeginConnect creates 3 additional (6 at
all) threads (as we can see in the task manager). As I understand this
method should use asynchronous call through a driver. Any way it shouldn't
use 3 additional thread - it should use only 1!

There are similar results with others BeginXXX methods, for example
FileStream.BeginRead use 2 additional threads.
It's looks like a seriouse bug in realisation of APM in .NET, or I'm wrong?

Thus it's more efficient to use something like this:

class Program
{
delegate void TestDelegate();
static Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
static void test()
{
socket.Connect("microsoft.com", 80);
}
static void Main(string[] args)
{

TestDelegate t = new TestDelegate(test);
IAsyncResult r = t.BeginInvoke(null, null);
t.EndInvoke(r);
Console.ReadLine();
}
}
In this example we have only 1 additional thread.
 
W

Walter Wang [MSFT]

Hi Alexander,

This is a quick note to let you know that I am performing research on this
issue and will get back to you as soon as possible. I appreciate your
patience.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
F

Fallen

Hi Alexander,

This is a quick note to let you know that I am performing research on this
issue and will get back to you as soon as possible. I appreciate your
patience.

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

This creates 3 threads:
Code:
using System;

public class Tester {
public static void Main(string[] args){
Console.ReadLine();
}
}

This creates 5 threads:
Code:
using System.Windows.Forms;

public class Tester {
public static void Main(string[] args){
Application.Run(new Form());
}
}
 
W

Walter Wang [MSFT]

Thanks Fallen for your input.

Hi Alexander,

If you test with a WinForm application instead of Console application, you
should see only one additional thread is used when using BeginConnect. I
believe this is because asynchronous socket uses windows message to notify
the caller that action is completed.

You mentioned that FileStream's BeginRead will also use two additional
threads, I don't observe this behavior on my side. Would you please
elaborate more? Thanks.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alexander Malapheev

Hi Walter,

I 've got absolutely another results for WinForms application. I had created
windows apllication (c#, .net 2.0) with one button and one label.
The code is:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Text =
System.Diagnostics.Process.GetCurrentProcess().Threads.Count.ToString();
}

private void button1_Click(object sender, EventArgs e)
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IAsyncResult res = sock.BeginConnect("smtp.gmail.com", 25, null, null);
sock.EndConnect(res);
sock.Disconnect(false);
label1.Text =
System.Diagnostics.Process.GetCurrentProcess().Threads.Count.ToString();
}
}

Ran this application without debugging.

Before pressing the button the label show that the process had 4 threads
(standard initial count of threads for WinForms application).

After the button is pressed the label shows 9 threads.

You wrote that in your WinForm application you had only 1 additional
thread, what was the total count of threads?

Becaus when you call BeginXXX method it doesn't create new threads if there
was free threads in thread pool.

If we rewrite this application:

public partial class Form1 : Form
{
delegate void TestDel();
void test()
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
sock.Connect("smtp.gmail.com", 25);
sock.Disconnect(false);
}
public Form1()
{
InitializeComponent();
label1.Text =
System.Diagnostics.Process.GetCurrentProcess().Threads.Count.ToString();
}

private void button1_Click(object sender, EventArgs e)
{
TestDel del = new TestDel(test);
IAsyncResult res = del.BeginInvoke(null, null);
del.EndInvoke(res);
label1.Text =
System.Diagnostics.Process.GetCurrentProcess().Threads.Count.ToString();
}
}

Then we will get 4 threads before and 7 threads after we pushed the button.

Thus we will see that in Console application and in WinForms application
BeginConnect method use 3 threads (in WinForms app we have also got 2
additional threads for somewhat else). And this is a realy looks like a bug.
 
P

Peter Duniho

[...]
Thus we will see that in Console application and in WinForms application
BeginConnect method use 3 threads (in WinForms app we have also got 2
additional threads for somewhat else). And this is a realy looks like a
bug.

Honestly, it's my opinion that you're making a mountain out of a
molehill. If you are using .NET, you have to accept that it will be doing
things behind the scenes. I would be surprised if the additional threads
you didn't expect are a) not intentional, and b) causing any sort of
problem.

Using the asynchronous Socket methods involves supporing IOCP under the
wrapper of .NET, at least on the OS versions that support IOCP. IOCP
works with threads. It's not unreasonable to expect .NET to create more
threads than it minimally needs; that's kind of the point of a thread
pool...to have several idle threads already created and waiting for work.

If it bothers you to have these extra threads, then you need to stop using
..NET. When you use .NET you don't have control over a wide variety of
things, including how it manages memory and threads. Just because you
don't understand why it's making the extra threads, that doesn't mean it's
a bug. It just means you don't understand why it's making the extra
threads.

Pete
 
W

Walter Wang [MSFT]

Thanks Peter for your input.

Hi Alexander,

First, the ProcessThread you're seeing via
Process.GetCurrentProcess().Threads is physical thread that is used by
current process. If you inspect its state, most of them have "Waiting"
state, how .NET and winsock use thread to perform asynchronous programming
are implementation detail which I cannot describe accurately here.

However, I can prove that there's really only one worker thread is used
when calling Socket.BeginConnect:

public Form1()
{
InitializeComponent();
label1.Text =
System.Diagnostics.Process.GetCurrentProcess().Threads.Count.ToString();
ShowThreads();
}

private void button1_Click(object sender, EventArgs e)
{
Socket sock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
IAsyncResult res = sock.BeginConnect("smtp.gmail.com", 25,
null, null);
while (!res.IsCompleted)
{
ShowAvailable();
Thread.Sleep(100);
}
sock.EndConnect(res);
sock.Disconnect(false);
label1.Text =
Process.GetCurrentProcess().Threads.Count.ToString();
}

void ShowThreads()
{
listBox1.Items.Clear();
foreach (ProcessThread t in Process.GetCurrentProcess().Threads)
{
listBox1.Items.Add(t.Id + ": " + t.ThreadState.ToString());
}

int workerThreads, ioThreads;

ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
listBox1.Items.Add(string.Format("{0}, {1}", workerThreads,
ioThreads));

ShowAvailable();
}

void ShowAvailable()
{
int workerThreads, ioThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out
ioThreads);
listBox1.Items.Add(string.Format("{0}, {1}", workerThreads,
ioThreads));
}


Above program will first list all thread id and its state, the last two
item in the ListBox shows currently max and available threads, on my side,
both are (50, 1000), which means there're no active worker/io threads
running.

After clicking button1 to call Socket.BeginConnect, we will periodically
check available threads and you should see constant (49,1000), until the
call is finished, it's back to (50,1000). This may not be 100% accurate,
but it should prove that there's only one worker thread is used.

Another note is, if you repeatedly click button1, the total ProcessThread
count will not increase, which should also prove that the "new" threads
you're seeing are really just some "lazy loading" threads that are only
created when first used.

Hope this helps.

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 

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