从在异步方法内启动的while循环退出

Arg*_*gon 1 c# asynchronous while-loop

如何打破异步方法内部的while循环。请参考下面的代码

我尝试了很多方法,但没有一个对我有用

async void TotalTimer(string time)
{
    while (true)
    {
        TimeSpan timesp = DateTime.Now - DateTime.Parse(time);
        TotalTime = timesp.Hours + " : " + timesp.Minutes + " : " + timesp.Seconds;
        await Task.Delay(1000);

        if (string.IsNullOrEmpty(time))
        {
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要停止并退出循环


更新的代码:

async Task TotalTimer(CancellationToken token)
{
    var intime = await App.Database.GetRecentIn();
    InTime = DateTime.Parse(intime.datetime).ToString("hh:mm tt");


    while (!token.IsCancellationRequested)
    {
        TimeSpan timesp = DateTime.Now - DateTime.Parse(intime.datetime);
        TotalTime = timesp.Hours + " : " + timesp.Minutes + " : " + timesp.Seconds;
        Console.WriteLine(TotalTime); // to see it's working
        await Task.Delay(1000);

        if (token.IsCancellationRequested)
        {
            break;
        }
    }
}

void StatCounting()
{
    var cts = new CancellationTokenSource();
   _= TotalTimer(cts.Token);
}

void StopCounting()
{
    var cts = new CancellationTokenSource();
    cts.Cancel();
   _= TotalTimer(cts.Token);

    _=Finalize();
}
Run Code Online (Sandbox Code Playgroud)

Ale*_*lex 5

CancellationToken像这样使用:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{
    class Program
    {
        private string TotalTime;

        static void Main(string[] args)
        {
            new Program().Run();
        }

        private void Run()
        {
            var cts = new CancellationTokenSource();
            TotalTimer("00:00:00", cts.Token);

            Console.ReadLine(); // waits for you to press 'Enter' to stop TotalTimer execution.
            cts.Cancel();
            Console.WriteLine("Press 'Enter' to exit the program.");
            Console.ReadLine();  // waits for you to press 'Enter' to exit the program. See, TotalTimer stopped.
        }

        // your original method modified
        async void TotalTimer(string time, CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                TimeSpan timesp = DateTime.Now - DateTime.Parse(time);
                TotalTime = timesp.Hours + " : " + timesp.Minutes + " : " + timesp.Seconds;
                Console.WriteLine(TotalTime); // to see it's working
                await Task.Delay(5000, token);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新资料

@Henk Holterman,根据编辑历史,if (string.IsNullOrEmpty(time))似乎是从外面打破循环的尝试。但这毫无意义,原因有二:

  • 字符串是不可变的
  • 如果time为null或为空DateTime.Parse(time)(在原始帖子中),则在检查之前抛出

将令牌添加到Task.Delay是一个好点。这样做可以节省资源,尽管对可观察的行为没有影响。

更新2

@Argon,代码独立于wpfwinformsconsole或什么的。参见wpf下面的最小示例。我检查了它的工作情况。如果某些操作不适用于您的具体代码,则您可能向我们隐藏了一些细节。

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

namespace WpfApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private string TotalTime;
        private CancellationTokenSource cts;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            cts = new CancellationTokenSource();
            TotalTimer("00:00:00", cts.Token);
        }

        async void TotalTimer(string time, CancellationToken token)
        {
            try
            {
                while (!token.IsCancellationRequested)
                {
                    TimeSpan timesp = DateTime.Now - DateTime.Parse(time);
                    TotalTime = timesp.Hours + " : " + timesp.Minutes + " : " + timesp.Seconds;
                    label1.Content = TotalTime;
                    await Task.Delay(5000, token);
                }
            }
            catch(OperationCanceledException)
            {
                label1.Content = "Canceled";
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            cts.Cancel();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当您有一个cancelToken时,您也可以将其传递给Task.Delay()。 (3认同)