C#任务被取消了

时间:2018-05-15 22:26:10

标签: c# task task-parallel-library

我编写了一个静态类来测试用户的互联网连接,并在连接状态发生变化时引发事件:

class InternetConnectionMonitor
{

    public static EventHandler<EventArgs<bool>> StatusChanged;

    private static bool isCancelled;
    private static bool isConnected;

    private static bool IsConnected
    {
        get
        {
            return isConnected;
        }
        set
        {
            if (isConnected != value)
            {
                StatusChanged(null, new EventArgs<bool>(value));
            }
            isConnected = value;
        }
    }

    public static async void Start(TimeSpan interval)
    {
        //TODO Use a 1st party webpage for connectivity testing.
        using (var client = new HttpClient())
        {
            client.Timeout = TimeSpan.FromSeconds(2);
            while (!isCancelled)
            {
                try
                {
                    await client.GetAsync("http://example.com");
                    IsConnected = true;
                }
                catch (Exception)
                {
                    IsConnected = false;
                }
                await Task.Delay(interval);
            }
        }
    }

    public static void Stop()
    {
        isCancelled = true;
    }
}

这个类非常有效,但是,在我的应用程序中使用TPL Dataflow进行其他一些处理时,在Start()方法的while循环中引发异常,说任务被取消了。我在这里发帖的原因是因为我从不取消任何任务。

这是我正在进行的处理。在QueueTests完成后,InternetConnectionMonitor中会引发任务取消异常,尽管QueueTests不会引用任何InternetConnectionMonitor。

如果我不调用validateProxies(),则永远不会引发异常。

private async void validateProxies(IEnumerable<Proxy> proxies)
{
    validateProxiesButton.Enabled = false;
    cancelValidatingProxiesButton.Enabled = true;
    addProxiesButton.Enabled = false;
    removeProxiesButton.Enabled = false;
    proxyTester = new ProxyTester();
    await proxyTester.QueueTests(proxies, judges);
    validateProxiesButton.Enabled = true;
    cancelValidatingProxiesButton.Enabled = false;
    addProxiesButton.Enabled = true;
    removeProxiesButton.Enabled = true;
    MessageBox.Show("Complete!");
}

public class ProxyTester
{

    private PauseOrCancelTokenSource pcts = new PauseOrCancelTokenSource();

    public async Task QueueTests(IEnumerable<Proxy> proxies, IEnumerable<ProxyJudge> judges, int maxConcurrency = 100)
    {
        var testProxies = new ActionBlock<(Proxy proxy, IProxyTest test)>((tup) =>
        {
            tup.proxy.Status = ProxyStatus.Testing;
            tup.proxy.Status = tup.proxy.TestValidity(tup.test);
        }, new ExecutionDataflowBlockOptions { CancellationToken = pcts.Token.CancellationToken, MaxDegreeOfParallelism = maxConcurrency });

        //Set each proxies status to Queued, and post to the dataflow block.
        foreach (var proxy in proxies)
        {
            proxy.Status = ProxyStatus.Queued;
            await testProxies.SendAsync((proxy, judges.GetRandomItem()));
        }

        testProxies.Complete();

        try
        {
            await testProxies.Completion;
        }
        catch (Exception)
        {

        }
    }

    public void Cancel()
    {
        pcts.Cancel();
    }

}

启动InternetConnectionMonitor(由JleruOHeP在评论中请求)

public proxyTesterView()
{
    InitializeComponent();

    InternetConnectionMonitor.StatusChanged += InternetConnectionMonitor_StatusChanged;
    InternetConnectionMonitor.Start(TimeSpan.FromSeconds(1));
}

private void InternetConnectionMonitor_StatusChanged(object sender, EventArgs<bool> e)
{
    if (e.Value == true)
    {
        MessageBox.Show("Online");
    }
    else
    {
        MessageBox.Show("Offline");
    }
}

1 个答案:

答案 0 :(得分:1)

解决了我自己的问题并希望分享我的解决方案。经过一番思考之后,我感觉线程池线程在调用QueueTests时可能会变得筋疲力尽,因为默认的最大并行度(100)很高。这个线程池耗尽似乎对客户端的调用产生了意想不到的副作用。在InternetConnectionMonitor的Start()方法中出现了GetAsync,导致请求超时不正确地触发,从而导致TaskCancelledException。

所以我产生了我自己的显式线程,并在其中同步执行测试。例外情况已经消失并且正在按预期工作。

public static void Start()
{
    //TODO Use a 1st party webpage for connectivity testing.
    var t = new Thread(() =>
    {
        while (!isCancelled)
        {
            try
            {
                var req = HttpWebRequest.Create("http://example.com");
                req.Timeout = 1000;
                using (var resp = req.GetResponse())
                {
                    resp.Close();
                }
                IsConnected = true;
            }
            catch (Exception ex)
            {
                IsConnected = false;
            }
            Console.WriteLine(IsConnected);
            Thread.Sleep(1000);
        }
    });
    t.Start();
}