C#中如何用Async/Await等待步进电机运动结束的状态变化
解决方案:用TaskCompletionSource实现异步等待电机停止
你的轮询方案虽用了await Task.Delay(1),但本质还是高频检查变量状态,会占用不必要的CPU资源,且不够优雅。更好的方式是用TaskCompletionSource<T>创建可等待任务,当Arduino返回停止信号时主动完成任务,实现真正的异步等待。
修改步骤:
- 添加TaskCompletionSource字段
在窗体类中声明对象跟踪电机运动的完成状态:
private TaskCompletionSource<bool> _motorCompletionTcs;
- 修改MoveTheMotor方法
每次启动电机时重置任务源,确保每次运动对应独立的等待任务:
public void MoveTheMotor(int position) { // 重置任务源,避免之前的任务影响当前操作 _motorCompletionTcs = new TaskCompletionSource<bool>(); int command = 1000; int movement = command + position; string com1 = movement.ToString(); port.Write(com1 + "\n"); movementFinished = false; }
- 更新Reply方法
收到Arduino的停止信号(MF1)时,完成任务源:
private void Reply(object sender, EventArgs e) { rispos = rispos.Trim(); // 先检查字符串长度,避免索引越界 if (rispos.Length >= 3 && rispos[0] == 'M' && rispos[1] == 'F' && rispos[2] == '1') { movementFinished = true; // 安全标记任务完成,避免重复调用 _motorCompletionTcs?.TrySetResult(true); } }
- 优化Demo方法
移除轮询逻辑,直接等待任务源的Task完成:
private async void Demo() { MoveTheMotor(800); try { // 异步等待电机完成,不会阻塞UI线程 await _motorCompletionTcs.Task; // 电机停止后执行后续操作 // 示例:更新UI、记录日志等 MessageBox.Show("电机运动已完成"); } catch (Exception ex) { // 处理异常(比如串口断开、超时等) MessageBox.Show($"运动过程中出现错误:{ex.Message}"); } }
额外优化建议:
- 添加超时处理:防止Arduino无响应导致程序一直等待,用
Task.WhenAny实现超时:
private async void Demo() { MoveTheMotor(800); var timeoutTask = Task.Delay(10000); // 10秒超时 var completedTask = await Task.WhenAny(_motorCompletionTcs.Task, timeoutTask); if (completedTask == _motorCompletionTcs.Task) { // 正常完成 MessageBox.Show("电机运动已完成"); } else { // 超时处理 MessageBox.Show("电机运动超时"); _motorCompletionTcs.TrySetCanceled(); // 清理任务源 } }
- 处理串口异常:在
risposta方法中捕获串口操作异常,避免程序崩溃并通知任务源:
private void risposta(object sender, SerialDataReceivedEventArgs e) { try { riceved = port.ReadLine(); rispos = riceved; this.Invoke(new EventHandler(Reply)); } catch (Exception ex) { // 将异常传递给等待的任务 _motorCompletionTcs?.TrySetException(ex); } }
内容的提问来源于stack exchange,提问作者Davide Floriani
相关产品推荐
相关产品推荐

