C# 在C语言中使用异步方法实现消息循环#

C# 在C语言中使用异步方法实现消息循环#,c#,asynchronous,async-await,message-loop,C#,Asynchronous,Async Await,Message Loop,我正在制作一个在线通信应用程序,我想异步处理消息。我发现异步等待模式在实现消息循环中很有用 以下是我到目前为止得到的信息: CancellationTokenSource cts=new CancellationTokenSource(); //This is used to disconnect the client. public Action<Member> OnNewMember; //Callback field async void NewMemberCallback

我正在制作一个在线通信应用程序,我想异步处理消息。我发现异步等待模式在实现消息循环中很有用

以下是我到目前为止得到的信息:

CancellationTokenSource cts=new CancellationTokenSource(); //This is used to disconnect the client.

public Action<Member> OnNewMember; //Callback field

async void NewMemberCallback(ConnectionController c, Member m, Stream stream){
    //This is called when a connection with a new member is established.
    //The class ConnectionController is used to absorb the difference in protocol between TCP and UDP.

    MessageLoop(c, m,stream,cts.Token);
    if(OnNewMember!=null)OnNewMember(m);
}

async Task MessageLoop(ConnectionController c, Member m, Stream stream, CancellationToken ct){
    MemoryStream msgbuffer=new MemoryStream();
    MemoryStream buffer2=new MemoryStream();

    while(true){
        try{
            await ReceiveandSplitMessage(stream, msgbuffer,buffer2,ct); //This stops until data is received.
            DecodeandProcessMessage(msgbuffer);
        catch( ...Exception ex){
            //When the client disconnects
            c.ClientDisconnected(m);
            return;
        }

    }
}
CancellationTokenSource cts=新的CancellationTokenSource()//这用于断开客户端的连接。
公共行动委员会成员//回调字段
异步void NewMemberCallback(ConnectionController c、成员m、流){
//当与新成员建立连接时,将调用此函数。
//ConnectionController类用于吸收TCP和UDP之间的协议差异。
MessageLoop(c、m、stream、cts.Token);
如果(OnNewMember!=null)OnNewMember(m);
}
异步任务MessageLoop(ConnectionController c、成员m、流、CancellationToken ct){
MemoryStream msgbuffer=新的MemoryStream();
MemoryStream buffer2=新的MemoryStream();
while(true){
试一试{
wait ReceiveandSplitMessage(stream,msgbuffer,buffer2,ct);//在收到数据之前停止此操作。
DecodeandProcessMessage(msgbuffer);
捕获(…异常示例){
//当客户端断开连接时
c、 客户端(m);
返回;
}
}
}
然后我得到一些警告,说在NewMemberCallback中,不等待对MessageLoop的调用。 我实际上不需要等待MessageLoop方法,因为该方法在连接断开之前不会返回。
像这样使用异步被认为是一种好的实践吗?我听说不等待异步方法是不好的,但我也听说我应该消除不必要的等待。或者对于消息循环使用异步模式被认为是不好的吗?

如果你不
等待MessageLoop(c,m,stream,cts.Token)
遇到第一个
wait
方法时,该方法将立即返回,然后执行该方法的其余部分。这将是一个触发并忘记的场景。UI线程上不会引发异常,因此如果
c.ClientDisconnected(m);
抛出它将在后台线程上引发,并导致显式吞并的异常,因为从方法返回的
任务
中存储的任何异常都不会被观察到。您可以在@nosratio中找到有关它的更多信息

老实说,这似乎有点不合常规


是否有更好的方法确定客户端是否已断开连接?您将错过任何可能要向用户或日志显示的重要异常。

通常,您希望跟踪已启动的任务,以避免再次进入并处理异常。例如:

Task _messageLoopTask = null;

async void NewMemberCallback(ConnectionController c, Member m, Stream stream)
{
    if (_messageLoopTask != null)
    {
        // handle re-entrancy
        MessageBox.Show("Already started!");
        return;
    }

    _messageLoopTask = MessageLoop(c, m,stream,cts.Token);

    if (OnNewMember!=null) OnNewMember(m);

    try
    {
        await _messageLoopTask;
    }
    catch (OperationCanceledException ex)
    {
        // swallow cancelation
    }
    catch (AggregateException ex) 
    { 
        // swallow cancelation
        ex.Handle(ex => ex is OperationCanceledException);
    }
    finally
    {
        _messageLoopTask = null;
    }
}
检查Lucian Wischik的


如果您可以有多个
MessageLoop
实例,那么您就不必担心重新进入,但您仍然希望观察异常情况。

您可以同时运行多个
MessageLoop
吗?我将为每个与之通信的客户端运行一个MessageLoop,因此将有许多MessageLoop实例在运行。然后将
\u messageLoopTask
设为局部变量或维护挂起任务的列表。您可能希望这样的列表在应用程序关闭时检查任务的状态。谢谢。我会试试。谢谢您的回答。我正在考虑将来自TcpClient的NetworkStream直接放入MessageLoop中的stream参数中,所以我想最简单的方法是检查断开连接时抛出的异常。我也可以使用TcpClient.Connected属性,但我不想进行轮询,这对我来说不是很直观,而且在某种程度上也会占用CPU。对此的一个小更正是:如果c.ClientDisconnected(m)抛出它将在后台线程上引发,并导致未处理的异常。它不会导致在后台线程上引发未处理的异常。相反,当
MessageLoop
任务被垃圾回收时,它将被隐式吞没,如前所述。