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

C#中async Task场景下关闭Form2触发TargetInvocationException的解决方法

问题修复方案

核心问题分析

  1. 静态CancellationTokenSource冲突:cts被定义为static,多Form2实例会共享同一个取消源,导致取消逻辑混乱。
  2. FormClosed事件处理错误:关闭窗体时重新创建新的CancellationTokenSource并调用Cancel,根本无法取消正在运行的DoSomeThing任务;反而再次调用DoSomeThing,完全偏离取消任务的目的。
  3. 任务访问已销毁控件:窗体关闭后label1已被销毁,后台任务仍尝试访问其Text属性,这是抛出TargetInvocationException的直接原因。
  4. UI操作效率低下:循环中反复解析label1.Text,既影响性能,也增加控件访问冲突概率。

修正后的代码

Form1代码(无需修改)

public Form1()
{
    InitializeComponent();
}

private void AddWindow_FormClosed(object sender, FormClosedEventArgs e)
{
    this.Show();
}

private void button1_Click(object sender, EventArgs e)
{
    Form2 ukl = new Form2();
    ukl.FormClosed += new FormClosedEventHandler(AddWindow_FormClosed);
    ukl.Show();
    this.Hide();
}

Form2修正代码

private CancellationTokenSource _cts;
private double _progress; // 用局部变量存储进度,避免反复访问UI控件

public Form2()
{
    InitializeComponent();
    _progress = 0;
}

private async void Form2_Load(object sender, EventArgs e)
{
    _cts = new CancellationTokenSource();
    try
    {
        await DoSomeThing(_cts.Token);
    }
    catch (OperationCanceledException)
    {
        // 任务被取消时的可选处理逻辑
    }
}

async Task DoSomeThing(CancellationToken token)
{
    while (_progress < 100 && !IsDisposed) // 检查窗体是否已销毁
    {
        token.ThrowIfCancellationRequested(); // 主动抛出取消异常,终止任务
        
        // 更新UI前先检查控件是否可用
        if (!label1.IsDisposed)
        {
            label1.Text = _progress.ToString("F0");
        }
        
        _progress += 1;
        await Task.Delay(100, token); // 传入token,让Delay也能响应取消
    }
}

private void Form2_FormClosed(object sender, FormClosedEventArgs e)
{
    // 取消并释放任务取消源
    _cts?.Cancel();
    _cts?.Dispose();
}

关键修改说明

  • 移除static修饰符:将cts改为实例私有字段_cts,每个Form2实例拥有独立的取消源,避免多实例冲突。
  • 局部变量存储进度:用_progress替代反复解析label1.Text,减少UI访问次数和字符串解析开销。
  • 正确取消任务:FormClosed事件中直接调用已有_cts的Cancel方法,同时释放资源,不再启动新任务。
  • 检查控件状态:循环中添加!IsDisposed和!label1.IsDisposed判断,避免访问已销毁的控件。
  • Task.Delay传入token:让Task.Delay响应取消请求,更快终止任务。
  • 主动抛出取消异常:使用token.ThrowIfCancellationRequested()替代简单return,确保任务正确终止并完成清理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 22:21:37