To get my GUI responsive while receiving data I (think I) need to implement the read of a BT device asynchronously.
But once I try to make it async by awaiting read
it's hanging in the first call or 2nd call.
Debugging also does not produce insights, breakpoint don't fire, etc.
When running synchronously the loop is usually running more than once.
public async Task<byte[]> ReadBufferFromStreamAsync(NetworkStream stream)
{
var totalRead = 0;
byte[] buffer = new byte[IO_BUFFER_SIZE];
while (!buffer.Contains((byte)'#'))
{
int read = await stream.ReadAsync(buffer, totalRead, buffer.Length - totalRead);
totalRead = read;
}
return buffer;
}
public async Task<string> readAsync()
{
string answer = System.Text.Encoding.UTF8.GetString(await ReadBufferFromStreamAsync(myStream));
Debug.Write(answer);
return answer.Trim(charsToTrim);
}
public async Task<string> WriteReadAsync(string str)
{
Debug.Write($"send:{str},");
await writeAsync(str);
var value = await readAsync();
Debug.Write($"received:{value}");
return value;
}
whereas this runs fine:
....
Task<int> read = stream.ReadAsync(buffer, totalRead, buffer.Length - totalRead);
totalRead = read.Result;
I would be also keen to know how you debug this kind of code in any case of trouble.
CodePudding user response:
As confirmed with your latest comment, you have an async deadlock. This question goes into a lot of detail about that and provides links to resources where you can learn more.
You say that somewhere in your code you have something like:
public void MyMethod()
{
MyOtherMethodAsync().Result;
}
This isn't really the right way to call async methods. You end up with a chicken and egg type situation, where MyMethod
needs to be free to receive the result of MyOtherMethodAsync
, so the async method basically waits to resume. The other side of this is MyMethod
's call to .Result
which is blocking, waiting for MyOtherMethodAsync
to complete. This ends up with them both waiting for each other, and then we have ourselves a deadlock.
The best solution is to make MyMethod
async:
public async Task MyMethod()
{
await MyOtherMethodAsync();
}
But this sometimes isn't possible. If the method has to be sync then you can use .ConfigureAwait(false)
to prevent it from hanging, but as Stephen Cleary notes, it is a hack. Anyway, you can do it like this:
public void MyMethod()
{
MyOtherMethodAsync().ConfigureAwait(false).GetAwaiter().GetResult();
}
Note that for UI event handler methods, you can change them to async void
:
public async void Button1_Clicked(object sender, EventArgs e)
{
await MyOtherMethodAsync();
}
But note that using async void
effectively means that you don't care about waiting for the result of the Button1_Clicked
method. It becomes fire and forget. This is, however, the recommended practise for integrating UI buttons, etc. with async methods.