如何在 Task.Delay 中退出 while 循环

How to get out a while loop when in Task.Delay

我试图在按下中止键时停止程序中的 while 循环,函数 运行ning 是 运行ning 一个 Task.Delay。不幸的是,尽管这一定很容易做到,但我就是无法让它为我工作。请帮忙。

我有一个按钮,要求用户确认他们想要 运行,如果是,它会进入下面的函数并开始 运行 RunSequence 函数。我确实在新线程上有这个,但现在已将其更改为任务,我保留了注释掉的代码以防万一我需要 运行 它而不是任务。 RunSequence 有两个参数,第二个是我认为我应该有的,那就是 CancellationToken。

CancellationTokenSource tokenSource = new CancellationTokenSource();

    private void ConfirmRunSequence()
    {
    //put it on a thread as the UI is slow to update
    //var thread = new Thread(() => { RunSequence(_filePathName, tokenSource.Token); });
    //thread.IsBackground = true;
    //thread.Start();
    
    Task.Run(() => RunSequence(_filePathName, tokenSource.Token), tokenSource.Token);
    }

当按下中止按钮时,我们将令牌设置为取消,我想退出 While 循环。

private void onAbort()
{
Abort = true; //set to abort sequence
            tokenSource.Cancel();
}

我希望上面的部分是正确的,我想接下来的部分是我不明白的。这里我有一个名为 _ct 的 CancellationToken,我认为它是 tokenSource。我在这里的延迟很大,所以当我看到标签更新几次时,我会点击中止,它会在我想取消的延迟内。现在这就是我无法开始工作的原因。

我在 _ct 下看到一条红色波浪线,上面写着“无法从 System.Threading.CancellationToken 转换为 System.Threading.Task.Task”。好的,我读了字,但抱歉,我不知道如何修复它,但我也不知道我是否修复了它,如果这是退出 While 循环的正确方法,请帮助。

private async void RunSequence(string filePath, CancellationToken _ct)
{
    Int count = 0;

while (!sr.EndOfStream) 
{
    lbl_count = count++;
    await Task.WhenAny(Task.Delay(10000), _ct);
}

lbl_count =”aborted”;
}

我尝试过的事情之一是从 await Task.WhenAny(Task.Delay(10000), _ct);到 只是 Task.Delay(10000, _ct) 但也不好。

而不是使用 Task.Delay,您可以通过超时访问 WaitHandle of the CancellationToken and call WaitOne

如果令牌被取消,WaitOne 操作的 return 值将为 true,如果达到超时,则为 false,然后您可以从中获取适当的进一步行动。

我制作了一个小应用程序来展示我是如何让我的应用程序工作的。创建一个带有两个按钮的小型 C# Winform .Net,一个用于 运行,一个用于中止和一个标签。由于有人要求提供代码,因此我在 Github

中包含了完整的示例程序

https://github.com/zizwiz/Cancellation_Token_Example

我还添加了下面一些代码的副本:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Cancel_Token_Example
{
    public partial class Form1 : Form
    {

        CancellationTokenSource tokenSource; // Declare the cancellation token

        public Form1()
        {
            InitializeComponent();
        }

        private void btn_run_Click(object sender, EventArgs e)
        {
            tokenSource = new CancellationTokenSource();    //Make a new instance
            Task.Run(() => RunSequence(tokenSource.Token)); //Run the task that we need to stop
        }

        private void btn_abort_Click(object sender, EventArgs e)
        {
            tokenSource.Cancel(); // make the token a cancel token
        }

        private async void RunSequence(CancellationToken _ct)
        {
            int counter = 0;

            while (!_ct.IsCancellationRequested)
            {
                // show incrementing number but as we have a task watch for cross threading
                WriteUIData((counter++).ToString());

                try
                {
                    await Task.Delay(1000, _ct); //waits 1 second
                }
                catch
                {
                    // Do nothing just needed so we can exit without exceptions
                }

            }

            if (_ct.IsCancellationRequested)
            {
                //report we have cancelled
                WriteUIData("Cancelled");
            }

            tokenSource.Dispose(); //dispose of the token so we can reuse
        }

        private void WriteUIData(String data)
        {
            // Write data to UI but as we have a task watch for cross threading

            if (lbl_output.InvokeRequired)
            {
                lbl_output.BeginInvoke((MethodInvoker)delegate () { lbl_output.Text = data; });
            }
            else
            {
                lbl_output.Text = data;
            }
        }
    }
}