如何为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
相关产品推荐
相关产品推荐

