【问题标题】:How to trigger an event upon progress bar completion如何在进度条完成时触发事件
【发布时间】:2016-09-27 19:59:59
【问题描述】:

我创建了一个 Windows 窗体程序,它分解文件并将其组件发送到服务器。这些文件很大,所以我创建了一个progressBar,这样用户就不会认为它在交易发生时冻结了。我想做的是有一些机制,只有当所有线程都完成而不阻塞 UI 线程时才会主动触发(同样,所以人们不会认为它被冻结了)。我能想到的最好的办法是一种被动的“等到真的”,但我觉得必须有更好的方法来做到这一点。我已经尝试过尝试创建一个事件或回调,但老实说,我最终比开始时更加困惑。这是我现在如何执行此操作的示例:

    public partial class Program : Form
    {
        private readonly OpenFileDialog _ofd = new OpenFileDialog();
        public delegate void BarDelegate();
        private string _path;

        private void button1_Click(object sender, EventArgs e)
        {
            if (_ofd.ShowDialog() != DialogResult.OK) return;

            textBox1.Text = _ofd.SafeFileName;
            _path = _ofd.FileName;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            var allLinesFromFile = File.ReadAllLines(_path);
            progressBar1.Minimum = 0;
            progressBar1.Maximum = allLinesFromFile.Length;

            Task.Factory.StartNew(() => Parallel.ForEach(allLinesFromFile, DoSomething));

            while (progressBar1.Value < progressBar1.Maximum) //there has to be a better way to do this...
            {
                MessageBox.Show("Please wait.", "Record Poster", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
            }

            //some processes here which should only take place after all threads are complete.

            var postingComplete = MessageBox.Show("The posting is complete!", "Record Poster", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
            if (postingComplete == DialogResult.OK) Environment.Exit(0);
        }

        private void DoSomething(string record)
        {
            //some string manipulation and server transactions here

            BeginInvoke(new BarDelegate(() => progressBar1.Increment(1)));
        }
    }

【问题讨论】:

  • 您是为进度条提供其值的人 - 因此,当您达到 100 时,调用您想要执行的方法。首先,您不需要 TaskFactory。
  • 第三 - 有多少行?似乎显示消息框比处理本身需要更多时间
  • 您可以编码ValueChanged事件并检查Value == Maximum
  • 消息框是否覆盖进度条?
  • @ShannonHolsinger 我使用了TaskFactory 来防止parallel.foreach 阻塞UI 线程。有数十万行。当您考虑服务器事务时,该程序可能需要 10-15 分钟来处理。另外,我设法让消息框不覆盖栏。

标签: c# multithreading events


【解决方案1】:

为此尝试使用 Microsoft 的反应式框架 (NuGet "System.Reactive.Windows.Forms")。那么你的代码就变成了:

    private void button2_Click(object sender, EventArgs e)
    {
        var allLinesFromFile = File.ReadAllLines(_path);
        progressBar1.Minimum = 0;
        progressBar1.Maximum = allLinesFromFile.Length;

        IDisposable subscription =
            allLinesFromFile
                .ToObservable()
                .SelectMany(f => Observable.Start(() => DoSomething(f)))
                .ObserveOn(this)
                .Do(x => progressBar1.Value += 1)
                .Subscribe(x => { }, () =>
                {
                    var postingComplete = MessageBox.Show("The posting is complete!", "Record Poster", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                    if (postingComplete == DialogResult.OK)
                    {
                        Application.Exit();
                    }
                });
    }

    private void DoSomething(string record)
    {
        System.Threading.Thread.Sleep(5);
    }

如果您需要尽早停止,请致电subscription.Dispose()。我已经对此进行了测试,对我来说效果很好。

【讨论】:

  • 我很欣赏这个答案,但这似乎是在所有线程完成之前处理订阅操作。有些计算只应在所有线程完成后进行,并且此解决方案将允许订阅块在最后一个线程被触发但在它实际发布到服务器之前运行。您可以告诉这一点,因为如果您使用大文件运行它,您会在栏到达末尾之前看到“发布完成”消息弹出。这与我尝试将方法调用添加到 ThreadPool 时遇到的问题相同,这就是我切换到 Parallel.Foreach 的原因。
  • Parallel.Foreach 阻止执行,直到所有线程完成,这是我想要的,但不幸的是它也阻止了 UI 的更新。因此我的困境。
  • 为你添加了一个后台工作者的想法......祝你好运!
  • @R.DevonNorth - “你可以告诉这个,因为如果你用一个大文件运行它,你会在栏到达末尾之前看到“发布完成”消息弹出。” - 不,那不是真的。 UI 的更新被推送到 UI 线程并在那里排队。处理绝对在消息框显示之前完成,但 UI 线程仍在忙于为进度条设置动画。它正在正确完成。
  • @Enigmativity 你是对的。使用此方法,完成消息会在栏完成前大约 1-2 秒出现。我认为这是我之前在使用 ThreadPool 时遇到的问题的症状,其中循环之后的代码是在所有线程运行它们的过程之前输入的,并且总计计算的信息不完整。但是,当我比较旧方法和这种方法之间计算的总数时,它们的结果是相同的。一个问题:为什么当我的 while-loop 方法没有时,这种方法会有那个差距?只要我的总数是正确的,这无关紧要:只是好奇。
【解决方案2】:

您应该使用 BackGroundWorker 类,请参阅:How to use a BackgroundWorker?

当线程结束时使用BackGroundWorker.RunWorkerComplete

【讨论】:

  • 我正在研究这个作为一种可能的解决方案。感谢您的建议。
【解决方案3】:

后台工作人员:

 **Backgroundworker (System.ComponentModel)**

 BackgroundWorker loader = new BackgroundWorker();
    loader.DoWork += load_Specials_BW_Thread;
        loader.WorkerReportsProgress = true;
        loader.ProgressChanged += load_Special_Feeds_Progress_Changed;
    private void load_Specials_BW_Thread(object sender, DoWorkEventArgs e)
    {

        int pctComplete = (int)Math.Floor(ptComplete * 100);//recs done / total recs
        (sender as BackgroundWorker).ReportProgress(pctComplete);
    }

祝你好运!

【讨论】:

    猜你喜欢
    • 2015-10-16
    • 2020-09-12
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多