【问题标题】:How to create percentage of loop(processing) [c#]如何创建循环百分比(处理)[c#]
【发布时间】:2016-12-21 10:39:26
【问题描述】:

示例:做某事 9999 次(可能超过)

for (int i = 1; i <= 9999; i++)
{
    // do something
    label1.content = 100*i/9999 + "%" ;
}

并且我想在编译时显示 label1 上的循环百分比我无法在几毫秒内执行任何操作,并且我的标签仅显示 100%。有人知道先生吗?谢谢。

【问题讨论】:

  • 很可能是因为您的代码阻塞了消息循环。如果你真的想要代码在不阻塞 UI 的情况下运行(例如阻塞更新),你需要在后台运行它。
  • 你的意思是我不能使用代码显示循环处理的百分比?

标签: c# wpf visual-studio loops percentage


【解决方案1】:

您不能同时在同一个线程上运行循环和更新 UI。这就是为什么您应该始终在后台线程上执行任何长时间运行的工作并使用调度程序定期更新 UI。

在后台线程上运行一些代码的最简单方法是使用任务并行库 (TPL) 来启动一个新任务:

Task.Run(()=> 
        {
            for (int i = 1; i <= 9999; i++)
            {
                System.Threading.Thread.Sleep(1500); //simulate long-running operation by sleeping for 1.5s seconds... 
                label1.Dispatcher.BeginInvoke(new Action(() => label1.Content = 100 * i / 9999 + "%"));
            }
        });

当百分比运行时我运行命令后,我的消息框立即显示消息

如前所述,任务正在另一个线程上执行。因此它将与 UI 线程并行运行。这就是在循环运行时可以更新 UI 的原因。

您可以使用 Task.ContinueWith 方法在任务完成后显示 MessageBox:

int i = 1;
Task.Run(() =>
            {
                for (; i <= 9999; i++)
                {
                    System.Threading.Thread.Sleep(1500); //simulate long-running operation by sleeping for 1.5s seconds... 
                    label1.Dispatcher.BeginInvoke(new Action(() => label1.Content = (i / 9999) * 100 + "%"));
                }
            }).ContinueWith(t =>
            {
              MessageBox.Show("done..." + i.ToString());
            }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());

【讨论】:

  • 我尝试使用它,但我发现了一些问题。 1.我添加 int number = 0 ;在Task.Run.......之前2.在for循环中我添加命令i++; 3.outside Task.Run....... I message box show i 4. message box show i = 0 it should be 9999
  • 我的消息框在百分比运行时运行命令后立即显示消息。
  • 请看我编辑的答案。您可能还想阅读多线程方面的内容。
【解决方案2】:

那是因为您在循环内计算时阻塞了 UI 线程。这样的事情应该用回调方法在不同的线程中进行。

作为一种解决方法,您可以导入system.windows.forms.dll 程序集并调用System.Windows.Forms.Application.DoEvents();

    for (int i = 1; i <= 9999; i++)
    {
        System.Windows.Forms.Application.DoEvents();
        // do something
        label1.Content = 100 * i / 9999 + "%";
        Thread.Sleep(10);
    }

但这是一种不好的做法!

【讨论】:

  • 您的解决方案没问题,这是 Tasks 之前的常规做法,但它需要在 WPF 应用程序中引用 System.Windows.Forms。
  • “这是任务之前的常规练习”,绝对不是。在 Tasks 之前,我们有 BackgroundWorker 和 ThreadPool。
  • 在我使用 Task.Yield() 方法的解决方案中,避免阻塞 UI 线程而不启动新线程是一种常规做法。
【解决方案3】:

@mm8 解决方案还可以,但是从新线程到 UI 线程的每次并发往返都会延迟您的循环。

如果您希望循环尽早结束而不会使 UI 膨胀(以及您的目标 NET Framework 4.5 或更高版本),您可以使用与旧的 DoEvents 等效的异步 Task 方法:Task.Yield()。

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < 10000; i++)
        {
            //Do something
            //Simulate work with Task.Delay(millisecondos)
            Label1.Content = (i * 100 / 10000) + "%";
            await Task.Yield(); //give opportunity to dispatch other events
        }
    }

这个解决方案很简单,是为什么异步不需要多线程的一个例子,正如this answerthis 中所述

但是在使用 Task.Yield() 和创建一个新线程之间需要权衡取舍,就像在 @mm8 解决方案中一样:

  1. 使用 Task.Yield() 休息会更好,以防工作可以分段完成,并且在每个部分中都必须更新 UI。
  2. 当 UI 更新很少(相对于工作完成)时,创建一个新线程会更好

注意。顺便说一句,在 10000 循环中,我认为最好显示千分之一的进度

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-14
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多