Task.IsCompleted无法返回正确的任务状态

时间:2018-07-17 17:06:55

标签: c# winforms async-await

我正在尝试捕获Form1_FormClosing事件期间长期运行的任务的状态。

长期运行的任务包括使用HttpClient进行异步/等待调用。

当我开始运行任务时,它会循环运行直到被取消。但是,当我在任务仍在运行的情况下关闭表单时,任务状态与预期不符,并显示状态RanToCompletionIsCompleted==true

我已经使用方法RunLongRunningMethodTest

复制了该问题

Task.Delay(2000);可以,但是await Task.Delay(2000);复制相同的问题。我不确定GetUrl由于GetAsync方法也必须是异步的。

如何修复下面的代码,以便Form1_FormClosing正确报告任务在运行时关闭仍在运行?我想检查任务状态,如果仍在运行,请取消,然后等待取消完成。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private static HttpClient client { get; set; }
        private Task task { get; set; }
        private CancellationTokenSource cts { get; set; }
        private bool buttonStartStopState { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private async void RunLongRunningMethodTest(CancellationToken cancellationToken)
        {
            try
            {
                while (true)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    Debug.WriteLine(DateTime.Now);
                    Task.Delay(2000); // task.IsCompleted = false - Works okay
                    //await Task.Delay(2000); // task.IsCompleted = true - Not correct
                }
            }
            catch (OperationCanceledException)
            {
                // Just exit without logging. Operation cancelled by user.
            }
            catch (Exception ex)
            {
                // Report Error
            }
        }

        private async void RunLongRunningMethod(CancellationToken cancellationToken)
        {
            try
            {
                while (true)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    var success = await GetUrl("https://www.bbc.co.uk/");
                    Thread.Sleep(2000);
                }
            }
            catch (OperationCanceledException)
            {
                // Just exit without logging. Operation cancelled by user.
            }
            catch (Exception ex)
            {
                // Report Error
            }
        }

        private async Task<bool> GetUrl(string url)
        {
            if (client == null)
            {
                client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true, AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip });
                client.BaseAddress = new Uri("https://www.bbc.co.uk/");
                client.DefaultRequestHeaders.Add("Accept", "*/*");
                client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
                client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/62.0");
                client.DefaultRequestHeaders.Add("Host", "https://www.bbc.co.uk/");
                client.DefaultRequestHeaders.Connection.Add("Keep-Alive");
                client.DefaultRequestHeaders.Add("DNT", "1");
            }

            var response = await client.GetAsync(url);
            var contents = await response.Content.ReadAsStringAsync();

            return true;
        }

        private void buttonStartStop_Click(object sender, EventArgs e)
        {
            buttonStartStopState = !buttonStartStopState;
            if(buttonStartStopState)
            {
                cts = new CancellationTokenSource();
                task = new Task(() => RunLongRunningMethod(cts.Token));
                task.Start();
            }
            else
            {
                cts.Cancel();
                cts = null;
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {

            if (task != null)
            {
                var taskStatus = $"{task.Status} {task.IsCompleted}";
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

第一个问题是您在长期运行的方法中使用async void。当代码到达await行时,该控件将交还给父级,并且由于“任务”没有其他要运行的内容,因此正确报告其已完成。

所以,我们来解决这个问题:

private async Task RunLongRunningMethodTest
private async Task RunLongRunningMethod

第二个问题是使用Task构造函数。不仅不鼓励使用它,而且在您的代码中也不必要。您应该这样做:

if (buttonStartStopState)
{
    cts = new CancellationTokenSource();
    task = RunLongRunningMethod(cts.Token); // will only compile after fixing the first problem
}

请注意,使用Task.Delay(2000);实际上是无用的,因为创建的任务被丢弃了。