挂在流中的 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 按钮等与异步方法集成的推荐做法。
为了在接收数据时让我的 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 按钮等与异步方法集成的推荐做法。