read from TCP/IP socket with blocking

J

Julie

So, I currently have code for reading in TCP/IP messages that looks
something like this:

StreamReader reader = new StreamReader(new NetworkStream(mySocket)));
while (true)
{
string inputString = reader.ReadLine();
_newMessages.Enqueue(inputString);
}

This is one thread in my application, and as it is a non-blocking
infinite loop, it is causing my application to consume 100% of my
CPU.

Is there a simple method of rewriting this to cause it to block when
there's nothing else to read? (thereby freeing up CPU)

I attempted to use StreamReader.ReadBlock(), but then realized that
this really doesn't block when the stream is empty either.

Thanks!
 
A

Andrew Poelstra

So, I currently have code for reading in TCP/IP messages that looks
something like this:

StreamReader reader = new StreamReader(new NetworkStream(mySocket)));
while (true)
{
string inputString = reader.ReadLine();
_newMessages.Enqueue(inputString);
}

This is one thread in my application, and as it is a non-blocking
infinite loop, it is causing my application to consume 100% of my
CPU.

Is there a simple method of rewriting this to cause it to block when
there's nothing else to read? (thereby freeing up CPU)

I attempted to use StreamReader.ReadBlock(), but then realized that
this really doesn't block when the stream is empty either.

Thanks!

You can create (a single) thread and use Thread.Sleep(). This also
lets you expand to allow multiple connections easily.
 
P

Peter Duniho

Julie said:
So, I currently have code for reading in TCP/IP messages that looks
something like this:

StreamReader reader = new StreamReader(new NetworkStream(mySocket)));
while (true)
{
string inputString = reader.ReadLine();
_newMessages.Enqueue(inputString);
}

This is one thread in my application, and as it is a non-blocking
infinite loop, it is causing my application to consume 100% of my
CPU.

That doesn't make sense. StreamReader.ReadLine() blocks until input is
available from the underlying Stream, which in turn should block until
input is available. It doesn't poll the socket (or at least, it should
not…my experience is that it does not).
Is there a simple method of rewriting this to cause it to block when
there's nothing else to read? (thereby freeing up CPU)

I attempted to use StreamReader.ReadBlock(), but then realized that
this really doesn't block when the stream is empty either.

You should post a concise-but-complete code example that demonstrates
the 100% CPU usage. I suspect that the problem is not in the code
you've shown, as StreamReader should be using the NetworkStream in a
blocking manner.

Pete
 
P

Peter Duniho

Andrew said:
You can create (a single) thread and use Thread.Sleep(). This also
lets you expand to allow multiple connections easily.

The Thread.Sleep() method is primarily for when a thread knows it has a
specific length of time it has to sleep. It can be used for yielding,
but that's almost always incorrect design.

In this particular example, there's nothing in the code posted to
suggest the thread isn't blocking correctly already, and all that
calling Thread.Sleep() will do is slow that thread down. Sure, it'll
use less CPU, but it'll do less work over a given amount of time too.

Pete
 
A

Andrew Poelstra

The Thread.Sleep() method is primarily for when a thread knows it has a
specific length of time it has to sleep. It can be used for yielding,
but that's almost always incorrect design.

In this particular example, there's nothing in the code posted to
suggest the thread isn't blocking correctly already, and all that
calling Thread.Sleep() will do is slow that thread down. Sure, it'll
use less CPU, but it'll do less work over a given amount of time too.

Pete

The code looked okay, but the OP claimed that it was spinning the CPU
and that a delay would be nice.

One project I did used Thread.Sleep() after some packets were sent
deliberately to slow the computer down, and give the client time
to process the request.
 
P

Peter Duniho

Andrew said:
The code looked okay, but the OP claimed that it was spinning the CPU

She did claim that.
and that a delay would be nice.

I don't think she did claim that.

I agree that the code as shown looks fine. Hence, IMHO we should not be
suggesting changes to that. Instead, we should insist on seeing the
code that's _not_ fine before making specific suggestions.
One project I did used Thread.Sleep() after some packets were sent
deliberately to slow the computer down, and give the client time
to process the request.

There are situations when it might be beneficial to throttle output.
For example, bandwidth restrictions on the recipient, or you'd like to
reduce network congestion when using UDP.

But those kinds of situations fall exactly into the category I described
as a valid use of Thread.Sleep(): you have a specific, known amount of
time for which it's important for the thread to delay.

There's no indication that the OP in this case would benefit from the
use of Thread.Sleep().

Pete
 
J

Julie

Hey guys,

So sorry - I posted this and then completely forgot to check the
responses! Thanks for responding.

I would not like to sleep in this thread because it actually does need
to operate as quickly as possible to process all of the TCP/IP input.

I was able to narrow it down to this thread causing my CPU usage. The
CPU was at 100%, then I froze the thread, and the CPU went back down.

That's interesting, Peter, that you believe ReadLine does block when
there's no incoming TCP/IP.

The code I posted that I'm using wasn't actually complete. Here is the
actual:

StreamReader reader = new StreamReader(new NetworkStream(mySocket)));
while (true)
{
string inputString = reader.ReadLine();
if (inputString != null)
{
_newMessages.Enqueue(inputString);
}
}

So, I'm actually checking for null, and I find that if there is no
incoming message, ReadLine() does not block and instead returns null.

Peter, are you able to run this same thing and find that ReadLine()
will block when there are no new messages? I can try to provide more
code if that's helpful.

Thanks! (and I promise this time not to forget about my post. :blush:)
 
P

Peter Duniho

Julie said:
[...]
The code I posted that I'm using wasn't actually complete. Here is the
actual:

StreamReader reader = new StreamReader(new NetworkStream(mySocket)));
while (true)
{
string inputString = reader.ReadLine();
if (inputString != null)
{
_newMessages.Enqueue(inputString);
}
}

So, I'm actually checking for null, and I find that if there is no
incoming message, ReadLine() does not block and instead returns null.

Ah. So it is possible in that loop for ReadLine() to return null.

ReadLine() returns null when you've reached the end of the stream. So,
basically your code causes the thread to go into an infinite loop once
reaching the end of the stream.

And yes, not only will that prevent your thread from ever getting any
further than that loop, but once reaching the end of the stream there is
nothing to restrict the execution of the thread, so CPU usage will hit 100%.

Without more context, it's not possible to know exactly what the right
thing to do is. But, given that the TCP connection can eventually be
closed, somehow you need to handle that appropriately.

That might be as simple as just exiting the thread when the return value
is null:

StreamReader reader = new StreamReader(new NetworkStream(mySocket));
string str;

while ((str = reader.ReadLine()) != null)
{
_newMessages.Enqueue(inputString);
}

But like I said, not possible to say for sure with the given information.

Pete
 
P

Peter Duniho

Peter said:
[...]
StreamReader reader = new StreamReader(new NetworkStream(mySocket));
string str;

while ((str = reader.ReadLine()) != null)
{
_newMessages.Enqueue(inputString);
}

Of course, the above only works if you use the same variable name for
the "string" variable everywhere. Should be "str" or "inputString"
only; mixing and matching won't compile. :)

Sorry for my sloppiness.

Pete
 
J

Julie

Peter said:
[...]
 StreamReaderreader = newStreamReader(new NetworkStream(mySocket));
  string str;
  while ((str = reader.ReadLine()) != null)
  {
    _newMessages.Enqueue(inputString);
  }

Of course, the above only works if you use the same variable name for
the "string" variable everywhere.  Should be "str" or "inputString"
only; mixing and matching won't compile.  :)

Sorry for my sloppiness.

Pete

But what if the client takes a break from sending but still has more
to send? I don't want to stop checking for messages, but I don't want
to use up CPU either. Hmm...
 
J

Julie

Perhaps, if I get null, then I can sleep; and if I don't get null, I
won't sleep(?).

Awesome! I just implemented this, and the CPU's no longer at 100%!

Thanks, everyone, for your ideas.
 
P

Peter Duniho

Julie said:
But what if the client takes a break from sending but still has more
to send? I don't want to stop checking for messages, but I don't want
to use up CPU either. Hmm...

As you may or may not have figured out by now, there's a big difference
between the end-of-stream, and temporary cessation of data being sent.

If the client "takes a break" by simply not sending more data,
ReadLine() will block. If the client "takes a break" by closing the
connection, then no more data will EVER arrive on that connection, and
ReadLine() will return null. In the latter case, there is no point in
continuing the loop. You'll NEVER see any more data returned by the
ReadLine() method of that instance of StreamReader.

If you have, as you seem to be implying in your subsequent replies,
added a call to Thread.Sleep() to your code, then you have NOT fixed the
problem correctly. You do NOT need Thread.Sleep() in this situation.

Pete
 
J

Julie

As you may or may not have figured out by now, there's a big difference
between the end-of-stream, and temporary cessation of data being sent.

If the client "takes a break" by simply not sending more data,
ReadLine() will block.  If the client "takes a break" by closing the
connection, then no more data will EVER arrive on that connection, and
ReadLine() will return null.  In the latter case, there is no point in
continuing the loop.  You'll NEVER see any more data returned by the
ReadLine() method of that instance ofStreamReader.

If you have, as you seem to be implying in your subsequent replies,
added a call to Thread.Sleep() to your code, then you have NOT fixed the
problem correctly.  You do NOT need Thread.Sleep() in this situation.

Pete


Ahhh...I didn't realize that null meant the client had closed the
connection. Thanks for the clarification.
 
J

Julie

Okay, so I've implemented what you recommended (only reading for as
long as inputString != null) - looks good! CPU is normal.
 

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