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

C# WinForms中无法通过按钮停止线程问题求助

问题解决:WinForms串口监控程序停止时抛出ObjectDisposedException

问题根源

你的代码核心问题出在btn_start_Click里的using var cts = _cts = new();:

  • using声明会在当前方法(btn_start_Click)执行完毕后自动释放CancellationTokenSource实例
  • 但你把这个实例赋值给了类级别的_cts字段,点击停止按钮时,调用的是一个已经被释放的CancellationTokenSource的Cancel()方法,自然会抛出ObjectDisposedException
  • 另外,线程里用Thread.Sleep(2500)会阻塞线程,即使取消信号触发,也要等sleep结束才能响应,导致停止不及时

修复方案(直接修正现有代码)

  1. 移除using声明,手动管理CancellationTokenSource的生命周期
  2. 用WaitHandle.WaitOne替代Thread.Sleep,让线程能即时响应取消信号
  3. 停止时正确释放资源

修正后的代码:

private CancellationTokenSource? _cts;
// 假设你已初始化SerialPort实例
private SerialPort? _serialPort;

private void btn_start_Click(object sender, EventArgs e)
{
    _cts = new CancellationTokenSource();

    try
    {
        Thread thread = new(() => serialserver(_cts.Token));
        thread.Start();
    }
    catch (Exception ex)
    {
        Invoke((MethodInvoker)(() => 
            txtbox_log.AppendText($"启动失败: {ex.Message}{Environment.NewLine}")
        ));
    }

    btn_start.Enabled = false;
    btn_stop.Enabled = true;
}

public void serialserver(CancellationToken token)
{
    Invoke((MethodInvoker)(() => 
    {
        txtbox_log.AppendText("Serial Server Started");
        txtbox_log.AppendText(Environment.NewLine);
    }));

    while (!token.IsCancellationRequested)
    { 
        Invoke((MethodInvoker)(() => 
        {
            txtbox_log.AppendText("TEST! I'm running");
            txtbox_log.AppendText(Environment.NewLine);
        }));

        // 用WaitHandle响应取消,代替Thread.Sleep,实现即时停止
        if (token.WaitHandle.WaitOne(2500))
        {
            break;
        }

        // 串口数据读取逻辑
        try
        {
            if (_serialPort?.IsOpen == true && _serialPort.BytesToRead > 0)
            {
                string data = _serialPort.ReadExisting();
                Invoke((MethodInvoker)(() => 
                    txtbox_log.AppendText($"收到数据: {data}{Environment.NewLine}")
                ));
            }
        }
        catch (Exception ex)
        {
            Invoke((MethodInvoker)(() => 
                txtbox_log.AppendText($"串口错误: {ex.Message}{Environment.NewLine}")
            ));
        }
    }

    Invoke((MethodInvoker)(() => 
    {
        txtbox_log.AppendText("Serial Server Stopped");
        txtbox_log.AppendText(Environment.NewLine);
    }));
}

private void btn_stop_Click(object sender, EventArgs e)
{
    if (_cts != null)
    {
        _cts.Cancel();
        _cts.Dispose();
        _cts = null;
    }

    // 停止时关闭串口
    if (_serialPort?.IsOpen == true)
    {
        _serialPort.Close();
    }

    btn_start.Enabled = true;
    btn_stop.Enabled = false;
}

更优实现方案(使用Task异步编程)

WinForms推荐用异步编程替代手动创建线程,代码更简洁且不阻塞UI:

private CancellationTokenSource? _cts;
private SerialPort? _serialPort;

private async void btn_start_Click(object sender, EventArgs e)
{
    _cts = new CancellationTokenSource();
    btn_start.Enabled = false;
    btn_stop.Enabled = true;

    try
    {
        await SerialServerAsync(_cts.Token);
    }
    catch (Exception ex)
    {
        txtbox_log.AppendText($"服务器异常: {ex.Message}{Environment.NewLine}");
    }
    finally
    {
        btn_start.Enabled = true;
        btn_stop.Enabled = false;
        _cts?.Dispose();
        _cts = null;
        _serialPort?.Close();
    }
}

private async Task SerialServerAsync(CancellationToken token)
{
    txtbox_log.AppendText("Serial Server Started" + Environment.NewLine);

    while (!token.IsCancellationRequested)
    {
        txtbox_log.AppendText("TEST! I'm running" + Environment.NewLine);
        
        // 异步等待,不阻塞UI线程
        await Task.Delay(2500, token);

        // 串口读取逻辑
        if (_serialPort?.IsOpen == true)
        {
            try
            {
                string data = _serialPort.ReadExisting();
                if (!string.IsNullOrEmpty(data))
                {
                    txtbox_log.AppendText($"收到数据: {data}{Environment.NewLine}");
                }
            }
            catch (Exception ex)
            {
                txtbox_log.AppendText($"串口读取错误: {ex.Message}{Environment.NewLine}");
            }
        }
    }

    txtbox_log.AppendText("Serial Server Stopped" + Environment.NewLine);
}

private void btn_stop_Click(object sender, EventArgs e)
{
    _cts?.Cancel();
}

额外注意事项

  • 串口操作优先用SerialPort.DataReceived事件,而非轮询,效率更高且避免线程阻塞
  • 所有UI操作必须在UI线程执行,异步代码中await会自动回到UI上下文,可直接操作控件
  • 程序关闭或停止时,务必关闭串口并释放所有资源,避免串口被占用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:25:13