挂在流中的 ReadAsync 中

hanging in ReadAsync from stream

为了在接收数据时让我的 GUI 响应,我(认为我)需要异步实现对 BT 设备的读取。

但是一旦我尝试通过等待 read 使其异步,它就会在第一次调用或第二次调用时挂起。 调试也不会产生洞察力,断点不会触发等

当 运行 同步时,循环通常 运行 不止一次。

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;
}

而这运行良好:

 ....
 Task<int> read =  stream.ReadAsync(buffer, totalRead, buffer.Length - totalRead);    
 totalRead +=  read.Result;

我也很想知道您是如何在遇到问题时调试此类代码的。

根据您的最新评论确认,您遇到了异步死锁。 This question 对此进行了很多详细介绍,并提供了资源链接,您可以在其中了解更多信息。

你说你的代码中某处有类似的东西:

public void MyMethod()
{
    MyOtherMethodAsync().Result;
}

这并不是调用异步方法的正确方法。您最终会遇到先有鸡还是先有蛋的情况,其中 MyMethod 需要空闲才能接收 MyOtherMethodAsync 的结果,因此异步方法基本上等待恢复。另一方面是 MyMethod.Result 的调用正在阻塞,等待 MyOtherMethodAsync 完成。这最终导致他们互相等待,然后我们自己陷入了僵局。

最好的解决办法是让MyMethod异步:

public async Task MyMethod()
{
    await MyOtherMethodAsync();
}

但这有时是不可能的。如果方法 必须同步 那么你可以使用 .ConfigureAwait(false) 来防止它挂起,但是作为 Stephen Cleary notes,这是一个 hack。无论如何,你可以这样做:

public void MyMethod()
{
    MyOtherMethodAsync().ConfigureAwait(false).GetAwaiter().GetResult();
}

请注意,对于 UI 事件处理程序方法,您可以将它们更改为 async void:

public async void Button1_Clicked(object sender, EventArgs e)
{
    await MyOtherMethodAsync();
}

但请注意,有效地使用 async void 意味着您不关心等待 Button1_Clicked 方法的结果。它变成火而忘记。然而,这是将 UI 按钮等与异步方法集成的推荐做法。