O botão Cancelar deve parar a execução. Se cada verificação de proxy capturar o cancelamento e retornar normalmente, o loop externo simplesmente avança para o próximo proxy. A tela então parece que Cancelar não fez nada.

No WinForms .NET moderno, mantenha uma única fonte como campo do formulário e deixe o cancelamento chegar ao manipulador de eventos:

private CancellationTokenSource? activeRun;

private async void startButton_Click(object sender, EventArgs e)
{
    if (activeRun is not null) return;
    using var run = new CancellationTokenSource();
    activeRun = run;
    startButton.Enabled = false;
    try
    {
        foreach (Uri proxy in configuredProxies)
        {
            run.Token.ThrowIfCancellationRequested();
            await CheckProxyAsync(proxy, run.Token);
        }
        statusLabel.Text = "Finished";
    }
    catch (OperationCanceledException) when (run.IsCancellationRequested)
    {
        statusLabel.Text = "Stopped";
    }
    finally
    {
        activeRun = null;
        startButton.Enabled = true;
    }
}

private void cancelButton_Click(object sender, EventArgs e)
    => activeRun?.Cancel();

configuredProxies é a lista de URI de proxy já analisada pelo aplicativo. Linhas da lista de proxies grátis podem alimentar uma verificação permitida de um alvo público; mantenha o protocolo e use apenas tipos compatíveis com este manipulador. A UI aguarda cada operação, então o loop de mensagens fica livre para receber o clique de cancelamento.

Leve o token até a rede

A verificação individual deve aceitar esse token. Uma verificação HTTP abreviada pode parecer assim:

private async Task CheckProxyAsync(Uri proxy, CancellationToken token)
{
    using var handler = new HttpClientHandler
    {
        Proxy = new System.Net.WebProxy(proxy),
        UseProxy = true
    };
    using var client = new HttpClient(handler);
    using var response = await client.GetAsync(
        probeUri, HttpCompletionOption.ResponseHeadersRead, token);
    response.EnsureSuccessStatusCode();
    string body = await response.Content.ReadAsStringAsync(token);
    RecordResult(proxy, body);
}

probeUri e RecordResult pertencem ao verificador existente. Mantenha um limite adequado de tamanho de resposta e um tempo limite para esse endpoint. Um verificador de produção pode reutilizar clientes por rota; o trecho mostra por onde o cancelamento viaja, não um projeto de pool de conexões.

Se os proxies com falha devem ser registrados e ignorados, capture as falhas de rede esperadas em torno da verificação individual. Deixe o cancelamento da execução escapar para o manipulador externo. Para verificações paralelas, compartilhe o token da execução com cada trabalhador e aguarde a conclusão deles antes de habilitar outra execução.

Artigos relacionados