如何在异步写入数据到客户端时防止此问题
The BeginWrite method cannot be called when another write operation is pending
Run Code Online (Sandbox Code Playgroud)
mycode的
public async void Send(byte[] buffer)
{
if (buffer == null)
return;
await SslStream.WriteAsync(buffer, 0, buffer.Length);
}
Run Code Online (Sandbox Code Playgroud)
and*_*i m 10
确切了解await关键字的作用非常重要:
await表达式不会阻止它正在执行的线程.相反,它会导致编译器将其余的异步方法注册为等待任务的延续.然后,Control返回到异步方法的调用者.当任务完成时,它会调用它的继续,异步方法的执行从它停止的地方恢复(MSDN - await(C#Reference)).
当您使用一些非空缓冲区调用Send时,您将进入
await SslStream.WriteAsync(buffer, 0, buffer.Length);
Run Code Online (Sandbox Code Playgroud)
使用等待您仅在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);
}
Run Code Online (Sandbox Code Playgroud)
并通过使用await调用它来等待它的完成:
await Send(sslStream, message);
Run Code Online (Sandbox Code Playgroud)
如果您不尝试从多个线程写入数据,这应该有效.
此外,还有一些代码可以防止多个线程的写操作重叠(如果与代码正确集成).它使用中间队列和异步编程模型(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);
}
Run Code Online (Sandbox Code Playgroud)