Is this an async reading? - c#

I have a listen() function which is reading the networkstream and a callback function newDataRecievedCallback.
I call a method BeginRead which is Async, but I call the same method in the callback function again. Isn't it then sync logic?
Is there anotherway to do it?
private void listen()
{
networkStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(newDataRecievedCallback), null);
}
private void newDataRecievedCallback(IAsyncResult rst)
{
try
{
int recievedDataSize = tcpClient.Client.Receive(buffer);
recievedData = convertToString(buffer, incomeDataSize);
//End Read
networkStream.EndRead(rst);
cleanBuffer();
parseXMLData(recievedData);
//Hier I call the same async method
networkStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(newDataRecievedCallback), null);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

If BeginRead always completes asynchronously then calling it again in the callback will still be asynchronous.
However BeginRead will sometimes complete synchronously (check IAsyncResult.CompletedSynchronously), thus your code is vulnerable to stack overflows when you get unlucky. For example, this could happen in one thread: newDataRecievedCallback -> BeginRead -> newDataRecievedCallback -> BeginRead and so on.
The proper way to use BeginRead is to use a pattern similar to the one below (this is a code snippet from C# 4.0 in a Nutshell). In essence, you should always check if the method completed synchronously and then act appropriately.
void Read() // Read in a nonblocking fashion.
{
while (true)
{
IAsyncResult r = _stream.BeginRead
(_data, _bytesRead, _data.Length - _bytesRead, ReadCallback, null);
// This will nearly always return in the next line:
if (!r.CompletedSynchronously) return; // Handled by callback
if (!EndRead (r)) break;
}
Write();
}
void ReadCallback (IAsyncResult r)
{
try
{
if (r.CompletedSynchronously) return;
if (EndRead (r))
{
Read(); // More data to read!
return;
}
Write();
}
catch (Exception ex) { ProcessException (ex); }
}
bool EndRead (IAsyncResult r) // Returns false if there’s no more data
{
int chunkSize = _stream.EndRead (r);
_bytesRead += chunkSize;
return chunkSize > 0 && _bytesRead < _data.Length; // More to read
}

It is still asynch because your call to networkStream.BeginRead does not block. You make the call and then exit the function. Yes, it will be called again but still in an asynch manner.
Is there another way? Yes, hundreds of ways. Your code isn't bad. It just seems a bit tightly coupled in that your asynch handler is performing its own management as well as processing data. A cleaner way would be to have some sort of controller which your newDataRecievedCallback would notify via a delegate, and pass the data to it for processing. The controller would also be responsible for spawning the next asynch process. A separate controller could also pass the received data for processing without blocking more asynch calls.

Related

Understanding await and async in .NET

I am working on refactoring code that uses the Bootstrap protocol to update the firmware of several nodes in a machine. The current code looks something like this (pseudo-code):
public void StartUpdate()
{
Sokcet bootpSocket = new Socket():
StateObject bootpState = new StateObject(bootpSocket);
BOOTPReceive(bootpState);
SendMagicPacket();
while (!IsError && !IsUpdateComplete)
{
//wait for BOOTP/Update to finish before returning to caller
}
}
private void BOOTPReceive(object state)
{
bOOTPSocket.BeginReceive(PACKET_DATA, 0, PACKET_DATA.Length, 0, OnBOOTPReceive, state);
}
SendMagicPacket()
{
//create and send magic packet
// this will tell the node to respond with a BOOTPPacket
}
private void OnBOOTPReceive(IAsyncResult result)
{
StateObject state = (StateObject) result.AsyncState;
Socket handler = state.workSocket;
int bytesRcvd = handler.EndReceive(result);
packet = PACKET_DATA;
if(isValidBOOTP(packet))
{
SendBOOTPResponse();
}
else{
BOOTPReceive(); //keep listening for valid bootp response
}
}
private void SendBOOTPResponse()
{
UdpClient udpClient = new UdpClient();
udpClient.BeginSend(packetData, packetData.Length, BROADCAST_IP, (int)UdpPort.BOOTP_CLIENT_PORT, OnBOOTPSend, udpClient);
}
private void OnBOOTPSend(IAsyncResult result)
{
UdpClient udpClient = (UdpClient)result.AsyncState;
int bytesSent = udpClient.EndSend(result);
udpClient.Close();
}
What I want to do is convert this to async-await but still require that I don't return back to the caller right away. How would I go about doing this? Is this possible to do? And would this be the right thing to do since await-async propagates all the way to the top?
Pseudo-code of what I think this would look like:
public void StartUpdate()
{
bool result = await SendMagicPacket();
bool IsError = await BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
//don't return to caller until BOOTPCommunication is completed. How do i do this?
}
You need to wait for the two tasks try the following:
public async Task StartUpdate()
{
var resultTask = SendMagicPacket();
var isErrorTask = BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
await Task.WhenAll(new[]{resultTask, isErrorTask});
//don't return to caller until BOOTPCommunication is completed. How do i do this?
}
//wait for BOOTP/Update to finish before returning to caller
You don't need any async IO at all because you want to wait until all operations are done. I assume you have copied some sample code. Most sample code uses async socket APIs.
Switch everything over to synchronous socket APIs and you're done.
If you want to keep this async for some reason you can indeed switch to await and untangle this code. The pseudo-code you posted looks like a good goal. It forces the surrounding method to be async Task, though.
You can deal with that by making all callers recursively async as well. If you don't need to conserve threads you could block on that task and have a mostly synchronous call chain. At that point you lose all async benefits, though.
Radin was on the right track, but I think what you want is something like this:
You need to wait for the two tasks try the following:
public async Task StartUpdate()
{
var resultTask = SendMagicPacket();
var isErrorTask = BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
Task.WhenAll(new[]{resultTask, isErrorTask}).Wait(); //Wait() will block so that the method doesn't return to the caller until both of the asynchronous tasks complete.
}
What that allows is SendMagicPacket and BOOTPCommunication to both fire simultaneously, but to wait for BOTH to complete. Using that pattern you can fire of N events simultaneously, while using Wait() to wait for all to finish so that the method itself returns synchronously.

Force Cancel Task with API that might hang

I am currently working with a Serial Port, and the API I use will some times hang on a read, even when its own time out is set.
This is not a big problem, but i need to do some work when that happens and the hanging thread needs to be shutdown. I have tried that with the following, but it has been giving me problems as the API call is not terminated, but allowed to continue while the rest of the code continues, and the TimeoutException was thrown. How can i use Tasks to be able to cancel a hanging task after a certain amount of time?
CancellationToken token = new CancellationToken();
var task = Task.Factory.StartNew(() =>
{
CallingAPIThatMightHang(); // Example
}, token);
if (!task.Wait(this.TimeToTimeOut, token))
{
throw new TimeoutException("The operation timed out");
}
CancellationToken is of the form of cooperative cancellation. You need to moderate the token while executing your operation and watch if a cancelation has been requested.
From your code block, it seems as you have one long running synchronous operation which you offload to a threadpool thread. If that's the case, see if you can separate that serial call to chunks, where you can poll the token after read chunk. If you can't, cancellation wont be possible.
Note that in order to request cancellation, you'll have to create a CancellationTokenSource, which you'll later be able to call it's Cancel() method.
As a side note, serial port is async IO, You can use naturally async API's instead of offloading a synchronous to a threadpool thread.
Edit:
#HansPassant gave a better idea. Run the third party call inside another process, one which you keep a reference to. Once you need to terminate it, kill the process.
For example:
void Main()
{
SomeMethodThatDoesStuff();
}
void SomeMethodThatDoesStuff()
{
// Do actual stuff
}
And then launch it in a separate process:
private Process processThatDoesStuff;
void Main()
{
processThatDoesStuff = Process.Start(#"SomeLocation");
// Do your checks here.
if (someCondition == null)
{
processThatDoesStuff.Kill();
}
}
If you need to communicate any result between these two processes, you can do those via several mechanisms. One would be writing and reading the Standard Output of the process.
I am sadly not able to use any other framework, and i am not able to just change the API i am calling so it can use a Cancellation Token.
This is how i chose to solve the problem.
class Program
{
static void Main(string[] args)
{
try
{
var result = TestThreadTimeOut();
Console.WriteLine("Result: " + result);
}
catch (TimeoutException exp)
{
Console.WriteLine("Time out");
}
catch (Exception exp)
{
Console.WriteLine("Other error! " + exp.Message);
}
Console.WriteLine("Done!");
Console.ReadLine();
}
public static string TestThreadTimeOut()
{
string result = null;
Thread t = new Thread(() =>
{
while (true)
{
Console.WriteLine("Blah Blah Blah");
}
});
t.Start();
DateTime end = DateTime.Now + new TimeSpan(0, 0, 0, 0, 1500);
while (DateTime.Now <= end)
{
if (result != null)
{
break;
}
Thread.Sleep(50);
}
if (result == null)
{
try
{
t.Abort();
}
catch (ThreadAbortException)
{
// Fine
}
throw new TimeoutException();
}
return result;
}
}

large number of threads is wait state in threadpool causing performance problems

My application connects to a large number of clients over http, downloads data from those clients and processes data as these results are received. Each request is sent in a separate thread so that the main thread does not remain occupied.
We have started encountering performance issues and it seems like these are mostly related to large number of threads in the ThreadPool that are just waiting for getting data back from those requests. I know with .NET 4.5 we have async and await for the same type of problem but we are still using .NET 3.5.
Any thoughts on what's the best way of sending these requests in a different thread but not to keep that thread alive while all its doing is to keep waiting for request to come back?
You can use async operations in .NET 3.5, it's just not as convenient as in .NET 4.5. Most IO methods have a BeginX/EndX method pair that is the async equivalent of the X method. This is called the Asynchronous Programming Model (APM).
For instance, instead of Stream.Read, you could use Stream.BeginRead and Stream.EndRead.
Actually, many async IO methods in .NET 4.5 are just wrappers around the Begin/End methods.
If you cannot use .NET 4.x and async/await, you still can achieve a sort of similar behavior using IEnumerator and yield. It allows to use pseudo-synchronous linear code flow with Begin/End-style callbacks, including statements like using, try/finally, while/for/foreach etc. You cannot use try/catch, though.
There are a few implementations of the asynchronous enumerator driver out there, e.g. Jeffrey Richter's AsyncEnumerator.
I used something like below in the past:
class AsyncIO
{
void ReadFileAsync(string fileName)
{
AsyncOperationExt.Start(
start => ReadFileAsyncHelper(fileName, start),
result => Console.WriteLine("Result: " + result),
error => Console.WriteLine("Error: " + error));
}
static IEnumerator<object> ReadFileAsyncHelper(string fileName, Action nextStep)
{
using (var stream = new FileStream(
fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1024, useAsync: true))
{
IAsyncResult asyncResult = null;
AsyncCallback asyncCallback = ar => { asyncResult = ar; nextStep(); };
var buff = new byte[1024];
while (true)
{
stream.BeginRead(buff, 0, buff.Length, asyncCallback, null);
yield return Type.Missing;
int readBytes = stream.EndRead(asyncResult);
if (readBytes == 0)
break;
// process the buff
}
}
yield return true;
}
}
// ...
// implement AsyncOperationExt.Start
public static class AsyncOperationExt
{
public static void Start<TResult>(
Func<Action, IEnumerator<TResult>> start,
Action<TResult> oncomplete,
Action<Exception> onerror)
{
IEnumerator<TResult> enumerator = null;
Action nextStep = () =>
{
try
{
var current = enumerator.Current;
if (!enumerator.MoveNext())
oncomplete(current);
}
catch (Exception ex)
{
onerror(ex);
}
enumerator.Dispose();
};
try
{
enumerator = start(nextStep);
}
catch (Exception ex)
{
onerror(ex);
enumerator.Dispose();
}
}
}

SslStream.WriteAsync "The BeginWrite method cannot be called when another write operation is pending"

How to prevent this issue when writing data to the client Asynchronously
The BeginWrite method cannot be called when another write operation is pending
MYCODE
public async void Send(byte[] buffer)
{
if (buffer == null)
return;
await SslStream.WriteAsync(buffer, 0, buffer.Length);
}
It is important to understand exactly what the await keyword does:
An await expression does not block the thread on which it is
executing. Instead, it causes the compiler to sign up the rest of the
async method as a continuation on the awaited task. Control then
returns to the caller of the async method. When the task completes, it
invokes its continuation, and execution of the async method resumes
where it left off (MSDN - await (C# Reference)).
When you call Send with some non null buffer you will get to
await SslStream.WriteAsync(buffer, 0, buffer.Length);
Using await you block the execution only in the Send method, but the code in the caller continues to execute even if the WriteAsync has not completed yet. Now if the Send method is called again before the WriteAsync has completed you will get the exception that you have posted since SslStream does not allow multiple write operations and the code that you have posted does not prevent this from happening.
If you want to make sure that the previous BeginWrite has completed you have to change the Send method to return a Task
async Task Send(SslStream sslStream, byte[] buffer)
{
if (buffer == null)
return;
await sslStream.WriteAsync(buffer, 0, buffer.Length);
}
and wait for it's completion by calling it using await like:
await Send(sslStream, message);
This should work if you do not attempt to write data from multiple threads.
Also here is some code that prevents write operations from multiple threads to overlap (if properly integrated with your code). It uses an intermediary queue and the Asynchronous Programming Model(APM) and works quite fast.
You need to call EnqueueDataForWrite to send the data.
ConcurrentQueue<byte[]> writePendingData = new ConcurrentQueue<byte[]>();
bool sendingData = false;
void EnqueueDataForWrite(SslStream sslStream, byte[] buffer)
{
if (buffer == null)
return;
writePendingData.Enqueue(buffer);
lock (writePendingData)
{
if (sendingData)
{
return;
}
else
{
sendingData = true;
}
}
Write(sslStream);
}
void Write(SslStream sslStream)
{
byte[] buffer = null;
try
{
if (writePendingData.Count > 0 && writePendingData.TryDequeue(out buffer))
{
sslStream.BeginWrite(buffer, 0, buffer.Length, WriteCallback, sslStream);
}
else
{
lock (writePendingData)
{
sendingData = false;
}
}
}
catch (Exception ex)
{
// handle exception then
lock (writePendingData)
{
sendingData = false;
}
}
}
void WriteCallback(IAsyncResult ar)
{
SslStream sslStream = (SslStream)ar.AsyncState;
try
{
sslStream.EndWrite(ar);
}
catch (Exception ex)
{
// handle exception
}
Write(sslStream);
}
If the operation was started with BeginWrite, call SslStream.EndWrite to end the old write operation before you start the next. If the operation was started using WriteAsync, ensure the Task has completed first, such as using the await keyword or Wait().
The other answers are fine but I think a more concise explanation (which is what I was looking for when I searched for the same error message) is this:
The method isn't thread-safe but is being called in a non-thread safe manner, because you aren't awaiting your Send method.

C#: How to set AsyncWaitHandle in Compact Framework?

I'm using a TcpClient in one of my Compact Framework 2.0 applications. I want to receive some information from a TCP server.
As the Compact Framework does not support the timeout mechanisms of the "large" framework, I'm trying to implement my own timeout-thing. Basically, I want to do the following:
IAsyncResult result = networkStream.BeginRead(buffer, 0, size, ..., networkStream);
if (!result.AsyncWaitHandle.WaitOne(5000, false))
// Handle timeout
private void ReceiveFinished(IAsyncResult ar)
{
NetworkStream stream = (NetworkStream)ar.AsyncState;
int numBytes = stream.EndRead(ar);
// SIGNAL IASYNCRESULT.ASYNCWAITHANDLE HERE ... HOW??
}
I'd like to call Set for the IAsyncResult.AsyncWaitHandle, but it doesn't have such a method and I don't know which implementation to cast it to.
How do I set the wait handle? Or is it automatically set by calling EndRead? The documentation suggests that I'd have to call Set myself...
Thanks for any help!
UPDATE
Seems that the wait handle is set automatically when calling EndRead - but it's not in the docs. Can somebody confirm this?
UPDATE 2
Wrote client.BeginRead in my sample code. Of course, BeginRead is called on the NetworkStream...
I think you have a misunderstanding about async IO with TCP.
To kick off async IO, call stream.BeginRead().
In the callback, you call EndRead on the stream.
You don't call BeginRead on the TcpClient, as your code shows. Your app doesn't ever signal the WaitHandle. The IO layer will invoke your callback when the waithandle is signalled, in other words when the async Read happens.
In your callback, normally you'd call BeginRead again, on the stream, if it's possible that you'll be receiving more data.
You can see a clear example in this answer.
Before starting the BeginRead/EndRead dance,
you may want to do an async Connect on the TcpClient - then you would use BeginConnect. But that's done just once. Alternatively, you might want a synchronous connect, in which case you just call TcpClient.Connect().
example code:
private class AsyncState
{
public NetworkStream ns;
public ManualResetEvent e;
public byte[] b;
}
public void Run()
{
NetworkStream networkStream = ...;
byte[] buffer = new byte[1024];
var completedEvent = new ManualResetEvent(false);
networkStream.BeginRead(buffer, 0, buffer.Length,
AsyncRead,
new AsyncState
{
b = buffer,
ns = networkStream,
e = completedEvent
});
// do other stuff here. ...
// finally, wait for the reading to complete
completedEvent.WaitOne();
}
private void AsyncRead(IAsyncResult ar)
{
AsyncState state = ar as AsyncState;
int n = state.ns.EndRead(ar);
if (n == 0)
{
// signal completion
state.e.Set();
return;
}
// state.buffer now contains the bytes read
// do something with it here...
// for example, dump it into a filesystem file.
// read again
state.ns.BeginRead(state.b, 0, state.b.Length, AsyncRead, state);
}

Categories

Resources