C#如何使用异步并等待?

时间:2019-06-14 06:42:17

标签: c# winforms async-await serial-port

我做串行通讯方法。它返回接收消息

首先,点击按钮

List<string> SendPkt = new List<string>();
List<string> RecvPkt = new List<string>();

private void btnRead_Click(object sender, EventArgs e)
{
    SendPkt.Clear();
    RecvPkt.Clear();

    C_Serial c_serial = new C_Serial();

    Trace.WriteLine("start : "+DateTime.Now);

    RecvPkt = c_serial.CommunicationJob(Port, Baud, SendPkt);

    for (int i = 0; i < RecvPkt.Count; i++)
        Trace.WriteLine(RecvPkt[i]);

    Trace.WriteLine("end : "+DateTime.Now);
}

C_Serial是...

public List<string> CommunicationJob(string port, int boud, List<string> sendPkt)
{
    string openResult = OpenPort(port, boud);
    string recvMsg = "";
    ....
    List<string> recvs = new List<string>();

    for (int i = 0; i < sendPkt.Count; i++)
    {
        recvMsg = SendAndRecv(sendPkt[i]);
        Trace.WriteLine("recvMsg : " + recvMsg);
        recvs.Add(recvMsg);
    }

    return recvs;
}
...
public string SendAndRecv(string sendPkt)
{
    string recvPkt = "";
    serialWrite(sendPkt);
    recvPkt = waitDataReceived();
    return recvPkt;
}

...

private string waitDataReceived()
{
    while(true)
    {
        // check about message recv complete
    }
}

waitDataReceived()具有无限循环,因此它必须作为线程工作

所以我这样使SendAndRecv()

public string SendAndRecv(string sendPkt)
{
    string recvPkt = "";
    serialWrite(sendPkt);
    Thread th = new Thread(() => recvPkt = waitDataReceived());
    th.Start();
    th.Join();
    return recvPkt;
}

但是Join()是无限循环时的块UI。

我对此进行了搜索,然后发现了异步/等待状态。

所以我修改了SendAndRecv()

public async Task<string> SendAndRecv(string sendPkt)
{
    string recvs = "";
    serialWrite(sendPkt);   
    recvs = await Task.Run(() => waitDataReceived());
    return recvs;
}

并且像这样在CommunicationJob()中使用

recvMsg = SendAndRecv(sendPkt[i]).Result;

但是它仍然阻止UI。即使无限循环中断,也不会释放。

也不要在CommunicationJob()中打印recvMsg。

程序停止。

如何使用线程不阻塞UI并等待直到返回值?

2 个答案:

答案 0 :(得分:2)

您需要整理模式,直到您的GUI单击方法为止。在继承行(调用层次结构)中,每种方法必须异步并使用await

public async Task<List<string>> CommunicationJob(string port, int boud, List<string> sendPkt)
{
    string openResult = OpenPort(port, boud);
    string recvMsg = "";
    ....
    List<string> recvs = new List<string>();

    for (int i = 0; i < sendPkt.Count; i++)
    {
        recvMsg = await SendAndRecv(sendPkt[i]);
        Trace.WriteLine("recvMsg : " + recvMsg);
        recvs.Add(recvMsg);
    }

    return recvs;
}

private async void btnRead_Click(object sender, EventArgs e)
{
    SendPkt.Clear();
    RecvPkt.Clear();

    C_Serial c_serial = new C_Serial();

    Trace.WriteLine("start : "+DateTime.Now);

    RecvPkt = await c_serial.CommunicationJob(Port, Baud, SendPkt);

    for (int i = 0; i < RecvPkt.Count; i++)
        Trace.WriteLine(RecvPkt[i]);

    Trace.WriteLine("end : "+DateTime.Now);
}

这应该确保UI不会冻结。

实际上,您可以在return语句之后直接写等待:

public async Task<string> SendAndRecv(string sendPkt)
{
    string recvs = "";
    serialWrite(sendPkt);
    return await Task.Run(() => waitDataReceived());;
}

答案 1 :(得分:1)

不仅必须使SendAndRecv异步,还必须使其所有呼叫者以及这些呼叫者的呼叫者(依此类推)async。并且,当您调用async方法时,您将使用await

因此,CommunicationJob应该是async

public async Task<List<string>> CommunicationJob(string port, int boud, List<string> sendPkt)
{
    string openResult = OpenPort(port, boud);
    string recvMsg = "";
    ....
    List<string> recvs = new List<string>();

    for (int i = 0; i < sendPkt.Count; i++)
    {
        recvMsg = await SendAndRecv(sendPkt[i]); // <--- note this line!
        Trace.WriteLine("recvMsg : " + recvMsg);
        recvs.Add(recvMsg);
    }

    return recvs;
}

并且btnRead_Click应该是async

private async void btnRead_Click(object sender, EventArgs e)
{
    SendPkt.Clear();
    RecvPkt.Clear();

    C_Serial c_serial = new C_Serial();

    Trace.WriteLine("start : "+DateTime.Now);

    RecvPkt = await c_serial.CommunicationJob(Port, Baud, SendPkt);

    for (int i = 0; i < RecvPkt.Count; i++)
        Trace.WriteLine(RecvPkt[i]);

    Trace.WriteLine("end : "+DateTime.Now);
}