You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework 4.8 WinForms启动任务后等待事件的异步实现问题

问题分析与解决方案

你的核心问题集中在UI线程阻塞和事件等待逻辑失效两点上,下面是针对性的修改方案和原因说明:

1. 修复UI线程阻塞:修改按钮点击事件

WinForms的UI事件允许使用async void(仅在UI事件场景下推荐),避免用GetAwaiter().GetResult()强制同步等待导致的冻结:

private async void btnLaunch_Click(object sender, EventArgs e)
{
    // 禁用按钮防止重复触发
    btnLaunch.Enabled = false;
    try
    {
        await CallAsync();
        // 后续可以添加完成后的UI提示逻辑
    }
    finally
    {
        btnLaunch.Enabled = true;
    }
}

2. 简化异步流程:重构CallAsync方法

去掉冗余的Task.Run和ContinueWith,用直白的顺序await实现分步等待,同时避免OCX跨线程访问风险(旧OCX通常依赖STA线程):

private async Task<bool> CallAsync()
{
    // 启动硬件进程
    await ruleEngine.ExecuteStep(new RuleEngine.Clases.Step() { MethodName = "Start", Parameters = null });
    // 等待硬件完成触发End事件
    await ruleEngine.ExecuteStep(new RuleEngine.Clases.Step() { MethodName = "End", Parameters = null });
    
    // 这里添加后续步骤,比如MoveAxis
    // await ruleEngine.ExecuteStep(new RuleEngine.Clases.Step() { MethodName = "MoveAxis", Parameters = new object[] { 75, 10 } });
    
    return true;
}

3. 修复事件等待失效:重构RuleEngine的End事件处理

原代码中全局TaskCompletionSource<T>只能触发一次,后续等待会直接返回,必须为每次事件等待创建独立的TCS:

public class RuleEngine
{
    private readonly List<object> _objects;

    public RuleEngine(List<object> objects)
    {
        _objects = objects;
    }

    public async Task<object> ExecuteStep(Step step)
    {
        foreach (var obj in _objects)
        {
            MethodInfo method = obj.GetType().GetMethod(step.MethodName);
            if (method != null)
            {
                if (method.ReturnType == typeof(Task))
                {
                    await (Task)method.Invoke(obj, step.Parameters?.ToArray());
                    return null;
                }
                else if (typeof(Task).IsAssignableFrom(method.ReturnType))
                {
                    return await ((dynamic)method.Invoke(obj, step.Parameters?.ToArray()));
                }
                else if (method.Name == "Start")
                {
                    // 直接调用Start方法(确保该方法仅启动进程而非阻塞)
                    return method.Invoke(obj, step.Parameters?.ToArray());
                }
            }
            else if (step.MethodName == "End")
            {
                var ocx = (AccessOCX)obj;
                // 为本次等待创建独立的TCS
                var eventTcs = new TaskCompletionSource<object>();

                // 定义临时事件处理程序
                void EndEventHandler(double executionTime, ResultConstants result)
                {
                    ocx.End -= EndEventHandler; // 先解绑避免重复触发
                    eventTcs.TrySetResult(null);
                }

                ocx.End += EndEventHandler;
                try
                {
                    await eventTcs.Task;
                    return true;
                }
                finally
                {
                    // 确保异常场景下也解绑事件,避免内存泄漏
                    ocx.End -= EndEventHandler;
                }
            }
        }

        Console.WriteLine($"Method {step.MethodName} not found");
        return null;
    }
}

关键修改说明

  • 全局TCS替换为局部TCS:原全局TCS在第一次触发后就处于完成状态,后续等待会直接跳过,无法响应新的End事件。每次等待创建独立TCS才能保证事件等待的正确性。
  • 移除Task.Run:旧OCX通常依赖STA线程(WinForms UI线程默认是STA),跨线程操作OCX可能导致事件不触发或异常,直接在UI线程上下文执行异步等待即可。
  • 事件处理程序及时解绑:避免OCX多次触发End事件导致逻辑混乱,同时防止内存泄漏。

内容的提问来源于stack exchange,提问作者Joe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:23:10