How to read data from a stream asynchronously in the background
I would like to read data from a stream (serial, tcp, whatever) asynchronously and fire events to notify other components.
Below is pseudo code assuming "stream" is a valid stream.
public class Reader {
private _stream;
private _buffer = new bytes[4096];
public event Action<byte[], int> DataRecieved;
public async void StartReading() {
while (true) {
var nbytes = await _stream.ReadAsync(_buffer, 0, _buffer.Length);
if (nbytes == 0)
return;
var handlers = DataRecieved;
if (handlers != null)
DataRecieved(_buffer, nbytes);
}
}
}
And the caller part:
var r = new Reader();
r.OnDataRecieved += myHandler;
r.StartReading();
I'm not sure if doing something like this is a good idea. I read that using asynchonous void functions is not a good idea, but here I do not want the caller to wait for the result, I want to notify him when some data is available.
What's a good way to do something like this?
source to share
void async
considered only used for GUI event handlers. WinForms
All types of void delegates are in events. Usually, when used async
, you notify your caller when you're done in an asynchronous manner. The .NET message context is considered an exception here as you have no other option to use async
.
In your case, the keywords async/await
don't make a lot of sense. I would recommend calling your method using Task or ThreadPool (or BackgroundWorker ).
You don't have a long running task that you want to respond to asynchronously, but a parallel background task that should be used as such.
The idea behind async / await is that the caller continues after the method has been called and can execute code within the method behind awaits later. But this requires that you use await on the call-method, which would block your main thread.
In short: you have no other chance to use a second thread and use thread synchronization.
Invoke
- this is nothing more than placing delegate
in the queue, which is a cycle of reading and reading. In your case, you can do something like this: take the read data, for example byte[]
, and put it in a queue (via an event). And whenever your main thread wants to do some work, it grabs an item from the queue. This is one of the options. The best solution for this problem depends a lot on your application, and since you haven't given us more design details, I can't recommend a better way. But async/await
it won't. Definitely.
source to share
Make a return method Task
to avoid async void
. You can ignore this task if you like, but in the end you probably want to wait for it to complete.
Also handle errors. They are discarded right now and the reading quietly stops. You will never find bugs this way.
Wrap everything in Task.Run
to make sure that this asynchronous method is indeed fully asynchronous. Right now, if everyone await
exits right away, this method will never return or return for a long time. Don't risk it. Alternatively, you can put await Task.Yield();
in the first line of the method.
source to share