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

如何为C#代理检查器实现取消按钮以终止异步任务

实现Windows Forms代理检查器的取消功能

要取消正在执行的异步代理检查任务,你需要借助CancellationTokenSource和CancellationToken传递取消信号,同时改造现有代码适配取消逻辑,具体实现如下:

1. 添加取消令牌管理字段

在Form类中声明CancellationTokenSource实例,用来统一控制所有异步任务的取消:

private CancellationTokenSource _cts;

2. 改造testProxy方法支持取消

修改方法签名,接受CancellationToken参数,绑定WebClient的取消逻辑,并处理取消异常:

public async Task testProxy(string ip, int port, CancellationToken token)
{
    bool OK = false;
    try
    {
        using (WebClient wc = new WebClient())
        {
            // 绑定取消信号:令牌触发取消时终止WebClient请求
            token.Register(() => wc.CancelAsync());
            
            wc.Proxy = new WebProxy(ip, port);
            await wc.DownloadStringTaskAsync(new Uri("http://google.com/ncr"));
            
            // 检查是否已取消,避免任务取消后仍执行后续逻辑
            token.ThrowIfCancellationRequested();
            
            OK = true;
            // 跨线程更新UI需切换到主线程
            if (richTextBox2.InvokeRequired)
            {
                richTextBox2.Invoke(new Action(() =>
                {
                    richTextBox2.Text += ip + ":" + port + "\n";
                }));
            }
            else
            {
                richTextBox2.Text += ip + ":" + port + "\n";
            }
            // 确保addgood()在主线程执行(如果涉及UI操作)
            if (InvokeRequired)
            {
                Invoke(new Action(addgood));
            }
            else
            {
                addgood();
            }
        }
    }
    catch (OperationCanceledException)
    {
        // 任务被取消,无需额外处理
    }
    catch (Exception)
    {
        // 处理代理无效、网络错误等其他异常
    }
}

3. 修改start_Click方法,启动异步任务并传递取消令牌

替换Task.WaitAll为await Task.WhenAll避免阻塞UI线程,同时初始化取消令牌:

private async void start_Click(object sender, EventArgs e)  //start checker
{
    if (proxy_list.Lines.Length < 1)
    {
        MessageBox.Show("Proxy list is Empty");
        return;
    }

    // 先终止之前未完成的任务
    _cts?.Cancel();
    _cts?.Dispose();
    _cts = new CancellationTokenSource();
    var token = _cts.Token;

    var asyncTasks = new List<Task>();
    foreach (var line in proxy_list.Lines)
    {
        if (string.IsNullOrWhiteSpace(line)) continue;
        
        var tab = line.Split(':');
        if (tab.Length != 2 || !int.TryParse(tab[1], out int port)) continue;
        
        asyncTasks.Add(testProxy(tab[0], port, token));
    }

    try
    {
        await Task.WhenAll(asyncTasks);
        MessageBox.Show("检查完成");
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show("检查已取消");
    }
    finally
    {
        _cts?.Dispose();
        _cts = null;
    }
}

4. 实现cancel_Click方法触发取消

在取消按钮点击事件中,触发取消信号并清理资源:

private void cancel_Click(object sender, EventArgs e) //stop checker
{
    _cts?.Cancel();
    _cts?.Dispose();
    _cts = null;
    MessageBox.Show("已触发取消");
}

完整修改后的代码

namespace WindowsFormsApplication7
{
    public partial class Form1 : Form
    {
        private CancellationTokenSource _cts;
        
        public Form1()
        {
            InitializeComponent();
        }

        public async Task testProxy(string ip, int port, CancellationToken token)
        {
            bool OK = false;
            try
            {
                using (WebClient wc = new WebClient())
                {
                    token.Register(() => wc.CancelAsync());
                    
                    wc.Proxy = new WebProxy(ip, port);
                    await wc.DownloadStringTaskAsync(new Uri("http://google.com/ncr"));
                    
                    token.ThrowIfCancellationRequested();
                    
                    OK = true;
                    
                    if (richTextBox2.InvokeRequired)
                    {
                        richTextBox2.Invoke(new Action(() =>
                        {
                            richTextBox2.Text += ip + ":" + port + "\n";
                        }));
                    }
                    else
                    {
                        richTextBox2.Text += ip + ":" + port + "\n";
                    }
                    
                    if (InvokeRequired)
                    {
                        Invoke(new Action(addgood));
                    }
                    else
                    {
                        addgood();
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // 任务取消,忽略
            }
            catch (Exception)
            {
                // 处理其他异常
            }
        }

        private void bunifuFlatButton1_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "TXT Files | *.txt";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string fileName = openFileDialog1.FileName;
                proxy_list.Text = File.ReadAllText(@fileName);
                proxy_list.Refresh();
            }
        }

        private async void start_Click(object sender, EventArgs e)  //start checker
        {
            if (proxy_list.Lines.Length < 1)
            {
                MessageBox.Show("Proxy list is Empty");
                return;
            }

            _cts?.Cancel();
            _cts?.Dispose();
            _cts = new CancellationTokenSource();
            var token = _cts.Token;

            var asyncTasks = new List<Task>();
            foreach (var line in proxy_list.Lines)
            {
                if (string.IsNullOrWhiteSpace(line)) continue;
                
                var tab = line.Split(':');
                if (tab.Length != 2 || !int.TryParse(tab[1], out int port)) continue;
                
                asyncTasks.Add(testProxy(tab[0], port, token));
            }

            try
            {
                await Task.WhenAll(asyncTasks);
                MessageBox.Show("检查完成");
            }
            catch (OperationCanceledException)
            {
                MessageBox.Show("检查已取消");
            }
            finally
            {
                _cts?.Dispose();
                _cts = null;
            }
        }

        private void cancel_Click(object sender, EventArgs e) //stop checker
        {
            _cts?.Cancel();
            _cts?.Dispose();
            _cts = null;
            MessageBox.Show("已触发取消");
        }
    }
}

关键说明

  • CancellationTokenSource生成统一的取消信号,所有任务共享同一令牌,点击取消按钮时调用Cancel()即可终止所有正在执行的任务。
  • WebClient通过token.Register(() => wc.CancelAsync())绑定取消逻辑,确保令牌触发时终止当前请求。
  • 使用await Task.WhenAll替代Task.WaitAll,避免阻塞UI线程,保证界面响应。
  • 所有UI更新操作需检查InvokeRequired,确保在主线程执行,避免跨线程操作异常。

内容的提问来源于stack exchange,提问作者Marlene E Coleman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 19:14:54