0

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.

Falco Alexander
  • 3,092
  • 2
  • 20
  • 39
  • 1
    One issue is that you're not `await`ing `ReadAsync`. And it's possibly some kind of async deadlock, but without seeing all the calls up the chain it's not easy to say where it's happening. – ProgrammingLlama Feb 22 '22 at 08:20
  • added it to the Q: awaiting the read does not change the behaviour. – Falco Alexander Feb 22 '22 at 08:24
  • No, but it is necessary. What calls `ReadBufferFromStreamAsync` and how? – ProgrammingLlama Feb 22 '22 at 08:25
  • added it to the Q – Falco Alexander Feb 22 '22 at 08:29
  • 1
    And how is `readAsync` getting called? Is there somewhere up the call hierarchy that _isn't_ `async` ? – ProgrammingLlama Feb 22 '22 at 08:31
  • see my modified Q. There is a point in the hierarchy where I use the async Task by waiting for the `.Result` – Falco Alexander Feb 22 '22 at 08:42
  • 1
    At that point in your code, you should either a) make that method `async` if you can, or b) call the task like `var result = myTask().ConfigureAwait(false).GetAwaiter().GetResult();`. If this solves your issue, then it's a deadlock. If the place where you're calling `.Result` is an event handler for UI, you can change it from `void` to `async void` (`async void` means the method can `await` a task, and that you don't care to wait for the response in the caller). – ProgrammingLlama Feb 22 '22 at 08:46
  • please copy your comment as an answer, I will accept it because my code already runs as expected after removing the last `.Result`, reason: exactly as you guessed. – Falco Alexander Feb 22 '22 at 08:54

1 Answers1

0

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.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • 2
    The `.ConfigureAwait(false)` is effective if you `await` the awaitable. If instead you block with `.GetAwaiter().GetResult()`, the `.ConfigureAwait(false)` [has no effect](https://stackoverflow.com/questions/54372407/still-confused-on-configureawaitfalse-used-with-getawaiter-and-getresult-in-c/54381160#54381160). – Theodor Zoulias Feb 22 '22 at 11:14