避免在 C# 中使用 Application.DoEvents()
Avoid Application.DoEvents() in C#
很多优秀的程序员(包括很多 Whosebug 的优秀成员)反对在任何情况下使用 Application.DoEvents()
。实际上它甚至在网络上有很多文章支持,比如 this one, this famous debate on SO, ...
不过,我被困在一个案例中,(我)认为 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;
}
不完全是这样,但这是一个示例代码,展示了我如何通过串行通信接收 sned-receive,你能告诉我我在哪里强迫自己陷入这个陷阱吗?
您应该使用 async
I/O 方法。 Asynchronously wait for Task<T> to complete with timeout 就是一个很好的例子。
如果您使用的是 .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>();
}
我假设答案是 运行 在不同的线程中循环并在响应可用时向 UI 发送消息。这假设您由于某种原因无法执行异步 IO
很多优秀的程序员(包括很多 Whosebug 的优秀成员)反对在任何情况下使用 Application.DoEvents()
。实际上它甚至在网络上有很多文章支持,比如 this one, this famous debate on SO, ...
不过,我被困在一个案例中,(我)认为 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;
}
不完全是这样,但这是一个示例代码,展示了我如何通过串行通信接收 sned-receive,你能告诉我我在哪里强迫自己陷入这个陷阱吗?
您应该使用 async
I/O 方法。 Asynchronously wait for Task<T> to complete with timeout 就是一个很好的例子。
如果您使用的是 .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>();
}
我假设答案是 运行 在不同的线程中循环并在响应可用时向 UI 发送消息。这假设您由于某种原因无法执行异步 IO