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

1 c# winforms async-await

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

\n\n

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

\n\n

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

\n\n

我已经使用方法复制了问题RunLongRunningMethodTest

\n\n

Task.Delay(2000);工作正常,但await Task.Delay(2000);重复了同样的问题。我不确定如何解决GetUrl由于 GetAsync 方法而必须异步的事实。

\n\n

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

\n\n
using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows.Forms;\n\nnamespace WindowsFormsApp1\n{\n    public partial class Form1 : Form\n    {\n        private static HttpClient client { get; set; }\n        private Task task { get; set; }\n        private CancellationTokenSource cts { get; set; }\n        private bool buttonStartStopState { get; set; }\n\n        public Form1()\n        {\n            InitializeComponent();\n        }\n\n        private async void RunLongRunningMethodTest(CancellationToken cancellationToken)\n        {\n            try\n            {\n                while (true)\n                {\n                    if (cancellationToken.IsCancellationRequested)\n                    {\n                        cancellationToken.ThrowIfCancellationRequested();\n                    }\n\n                    Debug.WriteLine(DateTime.Now);\n                    Task.Delay(2000); // task.IsCompleted = false - Works okay\n                    //await Task.Delay(2000); // task.IsCompleted = true - Not correct\n                }\n            }\n            catch (OperationCanceledException)\n            {\n                // Just exit without logging. Operation cancelled by user.\n            }\n            catch (Exception ex)\n            {\n                // Report Error\n            }\n        }\n\n        private async void RunLongRunningMethod(CancellationToken cancellationToken)\n        {\n            try\n            {\n                while (true)\n                {\n                    if (cancellationToken.IsCancellationRequested)\n                    {\n                        cancellationToken.ThrowIfCancellationRequested();\n                    }\n\n                    var success = await GetUrl("https://www.bbc.co.uk/");\n                    Thread.Sleep(2000);\n                }\n            }\n            catch (OperationCanceledException)\n            {\n                // Just exit without logging. Operation cancelled by user.\n            }\n            catch (Exception ex)\n            {\n                // Report Error\n            }\n        }\n\n        private async Task<bool> GetUrl(string url)\n        {\n            if (client == null)\n            {\n                client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true, AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip });\n                client.BaseAddress = new Uri("https://www.bbc.co.uk/");\n                client.DefaultRequestHeaders.Add("Accept", "*/*");\n                client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));\n                client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; \xe2\x80\xa6) Gecko/20100101 Firefox/62.0");\n                client.DefaultRequestHeaders.Add("Host", "https://www.bbc.co.uk/");\n                client.DefaultRequestHeaders.Connection.Add("Keep-Alive");\n                client.DefaultRequestHeaders.Add("DNT", "1");\n            }\n\n            var response = await client.GetAsync(url);\n            var contents = await response.Content.ReadAsStringAsync();\n\n            return true;\n        }\n\n        private void buttonStartStop_Click(object sender, EventArgs e)\n        {\n            buttonStartStopState = !buttonStartStopState;\n            if(buttonStartStopState)\n            {\n                cts = new CancellationTokenSource();\n                task = new Task(() => RunLongRunningMethod(cts.Token));\n                task.Start();\n            }\n            else\n            {\n                cts.Cancel();\n                cts = null;\n            }\n        }\n\n        private void Form1_FormClosing(object sender, FormClosingEventArgs e)\n        {\n\n            if (task != null)\n            {\n                var taskStatus = $"{task.Status} {task.IsCompleted}";\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

SO *_*ood 6

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

那么,让我们解决这个问题:

private async Task RunLongRunningMethodTest
private async Task RunLongRunningMethod
Run Code Online (Sandbox Code Playgroud)

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

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

作为补充说明,请注意 usingTask.Delay(2000);实际上是无用的,因为您正在创建一个被丢弃的任务。