将数据异步写入客户端时如何防止此问题
The BeginWrite method cannot be called when another write operation is pending
我的密码
public async void Send(byte[] buffer)
{
if (buffer == null)
return;
await SslStream.WriteAsync(buffer, 0, buffer.Length);
}
准确理解 await 关键字的作用很重要:
await 表达式不会阻塞它正在执行的线程。相反,它会导致编译器注册 async 方法的其余部分作为等待任务的延续。然后控制权返回给异步方法的调用者。当任务完成时,它调用它的继续,并且异步方法的执行从它停止的地方继续(MSDN - await(C# 参考))。
当您使用一些非空缓冲区调用 Send 时,您将到达
await SslStream.WriteAsync(buffer, 0, buffer.Length);
使用 await 您只能在 Send 方法中阻止执行,但即使 WriteAsync 尚未完成,调用者中的代码也会继续执行。现在,如果在 WriteAsync 完成之前再次调用 Send 方法,您将收到您发布的异常,因为 SslStream 不允许多个写入操作,并且您发布的代码不会阻止这种情况的发生。
如果要确保之前的 BeginWrite 已经完成,您必须更改 Send 方法以返回 Task
async Task Send(SslStream sslStream, byte[] buffer)
{
if (buffer == null)
return;
await sslStream.WriteAsync(buffer, 0, buffer.Length);
}
并通过使用 await 调用它来等待它的完成,例如:
await Send(sslStream, message);
如果您不尝试从多个线程写入数据,这应该可以工作。
这里还有一些代码可以防止来自多个线程的写操作重叠(如果与您的代码正确集成)。它使用中间队列和异步编程模型(APM)并且运行速度非常快。您需要调用 EnqueueDataForWrite 来发送数据。
ConcurrentQueue<byte[]> writePendingData = new ConcurrentQueue<byte[]>();
bool sendingData = false;
void EnqueueDataForWrite(SslStream sslStream, byte[] buffer)
{
if (buffer == null)
return;
writePendingData.Enqueue(buffer);
lock (writePendingData)
{
if (sendingData)
{
return;
}
else
{
sendingData = true;
}
}
Write(sslStream);
}
void Write(SslStream sslStream)
{
byte[] buffer = null;
try
{
if (writePendingData.Count > 0 && writePendingData.TryDequeue(out buffer))
{
sslStream.BeginWrite(buffer, 0, buffer.Length, WriteCallback, sslStream);
}
else
{
lock (writePendingData)
{
sendingData = false;
}
}
}
catch (Exception ex)
{
// handle exception then
lock (writePendingData)
{
sendingData = false;
}
}
}
void WriteCallback(IAsyncResult ar)
{
SslStream sslStream = (SslStream)ar.AsyncState;
try
{
sslStream.EndWrite(ar);
}
catch (Exception ex)
{
// handle exception
}
Write(sslStream);
}
如果操作是使用 BeginWrite 启动的,请调用SslStream.EndWrite以结束旧的写入操作,然后再开始下一个操作。如果操作是使用WriteAsync启动的,请确保任务首先完成,例如使用await
关键字或Wait()。
其他答案很好,但我认为更简洁的解释(这是我在搜索相同的错误消息时正在寻找的)是这样的:
该方法不是线程安全的,而是以非线程安全的方式调用的,因为您没有使用await
您的Send
方法。