취소 버튼은 실행을 중지해야 합니다. 각 프록시 검사가 취소를 잡고 정상적으로 반환하면 바깥쪽 루프는 그냥 다음 프록시로 넘어갑니다. 그러면 화면은 취소가 아무것도 하지 않은 것처럼 보입니다.

최신 .NET WinForms에서는 하나의 원본을 폼 필드로 유지하고 취소가 이벤트 처리기에 도달하게 하십시오:

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는 애플리케이션이 이미 구문 분석한 프록시 URI 목록입니다. 무료 프록시 목록의 행을 허용된 공개 대상 검사에 공급할 수 있습니다. 프로토콜을 유지하고 이 처리기가 지원하는 형식만 사용하십시오. UI는 각 작업을 기다리므로 메시지 루프는 취소 클릭을 받을 수 있습니다.

토큰을 네트워크까지 전달

개별 검사는 해당 토큰을 받아들여야 합니다. 축약된 HTTP 검사는 다음과 같을 수 있습니다:

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와 RecordResult는 기존 검사기에 속합니다. 이 엔드포인트에 적절한 응답 크기 제한과 시간 제한을 두십시오. 프로덕션 검사기는 경로별로 클라이언트를 재사용할 수 있습니다. 이 조각은 취소가 어디로 이동하는지를 보여 주며 연결 풀 설계는 아닙니다.

실패한 프록시를 기록하고 건너뛰어야 한다면 개별 검사 주변에서 예상되는 네트워크 실패를 잡으십시오. 실행 취소는 바깥쪽 처리기로 빠져나가게 하십시오. 병렬 검사의 경우 실행 토큰을 각 작업자와 공유하고 다른 실행을 활성화하기 전에 완료를 기다리십시오.

관련 글