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

C# 退出在异步方法内启动的while循环,c#,asynchronous,while-loop,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.Second

如何中断异步方法中的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;
        }
    }
}
我需要停下来退出循环

更新代码:

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();
}
像这样使用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);
            }
        }
    }
}
更新

@Henk Holterman,根据编辑历史记录,如果string.isNullOrEmptTime看起来像是试图从外部中断循环。但这毫无意义,原因有二:

字符串是不可变的 如果时间为null或空DateTime.Parsetime,则在检查之前,它在原始post中抛出 将令牌添加到任务中。延迟是一个很好的点。这样做可以节省资源,但对可观察到的行为没有影响

更新2

@此外,代码独立于wpf、winforms、console或其他任何东西。请参阅下面的最小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();
        }
    }
}
像这样使用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);
            }
        }
    }
}
更新

@Henk Holterman,根据编辑历史记录,如果string.isNullOrEmptTime看起来像是试图从外部中断循环。但这毫无意义,原因有二:

字符串是不可变的 如果时间为null或空DateTime.Parsetime,则在检查之前,它在原始post中抛出 将令牌添加到任务中。延迟是一个很好的点。这样做可以节省资源,但对可观察到的行为没有影响

更新2

@此外,代码独立于wpf、winforms、console或其他任何东西。请参阅下面的最小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();
        }
    }
}
在异步方法中使用中断是非常好的

这很好:

async void Main()
{
    await TotalTimer();
    Console.WriteLine("Done.");
}

async Task TotalTimer()
{
    while (true)
    {
        await Task.Delay(1000);

        if (true)
        {
            break;
        }
    }
}
它等待一秒钟,然后写入完成。到控制台

你的代码就是不改变时间,所以永远不会突破

除此之外,您的异步方法应该返回一个任务,而不是一个空。

在异步方法中使用中断是完全可以的

这很好:

async void Main()
{
    await TotalTimer();
    Console.WriteLine("Done.");
}

async Task TotalTimer()
{
    while (true)
    {
        await Task.Delay(1000);

        if (true)
        {
            break;
        }
    }
}
它等待一秒钟,然后写入完成。到控制台

你的代码就是不改变时间,所以永远不会突破


除此之外,您的异步方法应该返回一个任务,而不是一个空白。

感谢您的努力。我已经想出了一个阻止循环的方法。我已经定义了boolean,并在while循环中检查它以打破循环。这很可能是“谜”的答案

bool hasToStop = false;
while (true)
            {
                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 (hasToStop)
                {
                    Console.WriteLine("Stoped");
                    break;
                }
            }

谢谢你们的努力,伙计们。我已经想出了一个阻止循环的方法。我已经定义了boolean,并在while循环中检查它以打破循环。这很可能是“谜”的答案

bool hasToStop = false;
while (true)
            {
                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 (hasToStop)
                {
                    Console.WriteLine("Stoped");
                    break;
                }
            }

什么时候string.isNullOrEmptTime的退出条件会在这里为真?我看不到时间的值有任何变化,如果没有填充它以DateTime.Parsetime调用开始,则throw@Argon,此处的时间范围不允许其他方法在您显示的时间范围之外对其进行更新。@Argon,您认为您正在更改时间,但您没有,这就是问题所在。使用async void可能是一个错误的选择,通常是这样。您创建了两个TotalTimer,第二个带有预先取消的令牌。第一个将继续滴答作响。您还可以创建2个令牌源。一个不能阻止另一个。您的string.isNullOrEmptTime的退出条件何时会在这里为真?我看不到时间的值有任何变化,如果没有填充它以DateTime.Parsetime调用开始,则throw@Argon,此处的时间范围不允许其他方法在您显示的时间范围之外对其进行更新。@Argon,您认为您正在更改时间,但您没有,这就是问题所在。使用async void可能是一个错误的选择,通常是这样。您创建了两个TotalTimer,第二个带有预先取消的令牌。第一个将继续滴答作响。您还可以创建2个令牌源。一个不能阻止另一个。当你有一个cancelToken时,你最好将它传递给Task。还要延迟。我不理解投票被否决,但我怀疑你的代码就是op想要的。也就是说,取消正在运行的操作。您围绕问题编了一个MCVE,但丢失了if string.isNullOrEmptTime逻辑。这可能是造成混乱的原因。正如@HenkHolterman所说,我确实想解释一下为什么ops代码不起作用。答案有了很大的改进。我还建议不要捕获TaskCancelledException,而是OperationCancelledException,因为TaskCancelledException派生自它,所以您可以捕获它,但是CancellationToken.ThrowIfCancelled会抛出所述的一个。当您拥有cancelToken时,您还可以将其传递给Task.Delay。我不理解投票否决,但我怀疑你的代码就是op想要的。也就是说,取消正在运行的操作。您围绕问题编了一个MCVE,但丢失了if string.isNullOrEmptTime逻辑。这可能是困惑的根源。正如@HenkHolterman所说,我确实会
d解释ops代码不起作用的原因。答案有很大改进。我还建议不要捕获TaskCancelledException,而是OperationCancelledException,因为TaskCancelledException派生自它,所以您可以捕获它,但是CancellationToken.ThrowIfCancelled会抛出所述的一个。这对我不起作用。我已经更新了我的代码,请检查it@Argon-此外,请不要编辑您的问题,以免使现有答案看起来错误。除非您在编辑问题时出错,否则请始终在编辑问题时附加更新。最后,您的更新代码现在完全错误了。您应该只创建一个新的CancellationTokenSource,并且只调用TotalTimer一次。谢谢您的指导。下次我会做的。这对我不起作用。我已经更新了我的代码,请检查it@Argon-此外,请不要编辑您的问题,以免使现有答案看起来错误。除非您在编辑问题时出错,否则请始终在编辑问题时附加更新。最后,您的更新代码现在完全错误了。您应该只创建一个新的CancellationTokenSource,并且只调用TotalTimer一次。谢谢您的指导。下次我会做的。如果从其他线程更新变量hasToStop,则代码不安全。循环永远不会结束,因为工作线程可能会永远看到变量的缓存值。在这种情况下,您应该将变量声明为,或者在每次使用时锁定它。更好地使用.NET Framework提供的专用结构正是出于以下原因:。从另一个线程执行此操作不是一个好主意。token.IsCancellationRequested是正确的方法。如果从其他线程更新变量hasToStop,则代码不安全。循环永远不会结束,因为工作线程可能会永远看到变量的缓存值。在这种情况下,您应该将变量声明为,或者在每次使用时锁定它。更好地使用.NET Framework提供的专用结构正是出于以下原因:。从另一个线程执行此操作不是一个好主意。token.IsCancellationRequested是正确的方法。