避免使用C#中的Application.DoEvents()

时间:2015-02-06 01:48:57

标签: c# doevents

许多优秀的程序员(包括Stackoverflow的许多优秀成员)都反对在任何情况下使用Application.DoEvents()。实际上它甚至支持网上有大量文章,如this onethis famous debate on SO,......

虽然,我陷入了一个案例,其中(I)认为DoEvents()是唯一的退出(缺乏经验)。这就像介绍一样,让我们​​看一些编码。

我有一个' serialPort'组件通过串行通信与控制器连接,发送命令并等待其响应,这就是全部。

string response = "";
bool respFlag;

private string sendCommand(string command)
{
  respFlag = false;  //initialize respFlag

  serialPort1.Write(command);  // send the command

  Stopwatch timer = Stopwatch.StartNew(); // start a timer
  while(true)
  {
    // break from the loop if receive a response
    if(respFlag) break;  

    // timeOut error if no response for more than 500msec
    if(timer.ElapsedMilliseconds >= 500) break;

    // here comes the UGLY part
    Application.DoEvents(); 
  }

   return response;
}

在我的serialPort的DataReceived方法中,我读取了现有的响应并打破了循环

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  response = serialPort1.ReadExisting();

  // set the flag to true to exit the infinite loop in sendCommand
  respFlag = true; 
}

它不完全是这样的,但这是一个示例代码,显示了我如何通过串行通信接收,你能告诉我在哪里迫使自己陷入这个陷阱吗?

3 个答案:

答案 0 :(得分:4)

如果您使用的是.NET 4.5,那么使用async / await和TaskCompletionSource以及async / await非常容易。

TaskCompletionSource<string> resultTcs = new TaskCompletionSource<string>();
private async Task<string> SendCommandAsync(string command)
{
    serialPort1.Write(command);  // send the command

    var timeout = Task.Delay(500);

    //Wait for either the task to finish or the timeout to happen.
    var result = await Task.WhenAny(resultTcs.Task, timeout).ConfigureAwait(false);

    //Was the first task that finished the timeout task.
    if (result == timeout)
    {
        throw new TimeoutException(); //Or whatever you want done on timeout.
    }
    else
    {
        //This await is "free" because the task is already complete. 
        //We could have done ((Task<string>)result).Result but I 
        //don't like to use .Result in async code even if I know I won't block.
        return await (Task<string>)result;
    }
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var response = serialPort1.ReadExisting();
    tcs.SetResult(response);

    //reset the task completion source for another call.
    resultTcs = new TaskCompletionSource<string>();
}

答案 1 :(得分:0)

您应该使用async I / O方法。 Asynchronously wait for Task<T> to complete with timeout就是一个很好的例子。

答案 2 :(得分:0)

我假设答案是在不同的线程中运行该循环,并在响应可用时向UI发送消息。这假设您出于某种原因无法执行异步IO