【问题标题】:Time-consuming task in MainForm, progress bar in another formMainForm中的耗时任务,另一种形式的进度条
【发布时间】:2015-03-20 10:35:08
【问题描述】:

我有一个主要的 UI 来做一些耗时的工作。当它正在执行时,我想打开第二个带有进度条(选框样式)的表单以指示“正在处理它”。

我看到有人把耗时的任务放在BackgroundWorker,但是,我想在主 UI 线程中运行

耗时任务将在MainForm中执行。我想为各种进程重用进度条,所以我正在编写第二个表单ProgressBarForm,其中包含BackgroundWorker,它将在显示进度条的同时启动_mainWork,并将停止并关闭ProgressBarForm_mainWork 完成时。

因为表单是模态的,我正在考虑在BackgroundWorker 中显示ProgressBarForm,以免阻止MainForm

请注意,我不是BackgroundWorker 中运行mainForm。我的 backgroundWorker 只是显示表单并可能报告一个计时器。

public partial class ProgressBarFom : UControl
{
    public delegate void MainWork();
    private MainWork _mainWork;
    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //progressBar.Hide();
        this.OnClose(sender, e);
        //
        backgroundWorker.Dispose();
    }
        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            //show this ProgressBarForm
            this.ShowDialog();
            //stop backgroundWorker
            //calling this.Close() in RunWorkerComplete 

            if (backgroundWorker.CancellationPending == true)
            {
                e.Cancel = true;
                return;
            }
        }
        public void CallProgressBar(object sender, EventArgs e)
        {
            //progressBar.Show();
            backgroundWorker.RunWorkerAsync();

            _mainWork();
            if (backgroundWorker.IsBusy)
                backgroundWorker.CancelAsync();
        }
}

在 MainForm 中,我传递 mainwork 并调用 ExecWithProgressBar

private void ExecWithProgressBar()
    {
        ProgressBarFom .MainWork mainWork = new ProgressBarFom .MainWork(ProgressBarMainWork);
        ProgressBarFom prBar = new ProgressBarFom (mainWork);
        prBar.CallProgressBar(null, null);
    }

遇到的一些问题

  1. 在 DoWork 内部,也会出现同样的模式问题。 ShowDialog() 将阻塞线程,因此我永远不会检查 CancellationPending 来关闭 ProgressBarForm。

  2. ProgressBarForm 启动晚于 mainWork。我想当我调用 CallProgressBar 时,backgroundWorker 应该在我的 mainWork 之前启动。

  3. 在 RunWorkerComplete 中是否需要 worker.Dispose()?

  4. 在 Worker 中运行 mainWork 会更好吗?为什么?我决定让主线程运行这个以不干扰正常流程,主线程中的内容将保留在主线程中,进度条就像一个插件。如果我们把它带到工人那里,我们是否需要另一个线程来获取进度条本身?

【问题讨论】:

  • “我看到有人把耗时的任务放在BackgroundWorker中,但是我想在主UI线程中运行” -- 为什么?为什么要拒​​绝经验丰富的程序员的好建议和 .NET API 的预期设计?只要以正确的方式去做。不仅有更好的资源可以向您解释如何以正确的方式进行操作,而且您的程序只会更好地工作。与 API 抗争无疑是一种让您头疼的方法和非常糟糕的代码。
  • 我尝试将 _mainWork 放在 backgroundWorker 中,这会带来问题。我猜这是因为 _mainWork 使用了主线程中的一些东西。您建议如何克服这一点?
  • BackgroundWorker 及其后继者TaskProgress<T> 都不是灵丹妙药。您仍然可以做错并犯错误。但问题是:有 很多 在线资源可以帮助那些做错和犯错的人学习如何正确和不犯错误地做事。很少有在线资源旨在帮助人们成功地做错事,而且很少有人愿意努力创建此类资源。如果您在让BackgroundWorker 工作时遇到问题,请寻求帮助以正确处理。你目前的做法注定要失败。
  • 谢谢彼得。我确实做了一些功课,不幸的是网上的例子和教程往往停留在“//模拟一些耗时的任务:DoWork{System.Threading.Thread.Sleep(5000);}”你能详细说明如何在后台工作人员中正确调用_mainwork ?它会破坏我使用工作线程但反过来要求主线程完成工作的目的吗?我希望这个进度条表单可以重复使用,它可以与我输入的任何方法一起运行。这就是我尝试将 _mainWork 输入到 BackgroundWorker 的原因。
  • “它会破坏我使用工作线程但反过来要求主线程完成工作的目的” - 是的。 “我希望这个进度条表单可以重复使用”——就像ProgressBar 控件可以重复使用一样,任何其他进度 UI 实现也可以重复使用。至于详细说明“如何在后台工作人员中调用_mainwork”,如果您尝试一下,效果会更好,然后发布一个问题,在其中解释该尝试并描述您的文档和教程已经使用过,并寻求帮助。否则,答案会变得过于宽泛。

标签: c# multithreading modal-dialog progress-bar progressdialog


【解决方案1】:

除非你做了一些非常丑陋的 hack(比如在你的应用程序中运行多个消息循环),否则如果运行主窗口的线程很忙,你将无法显示对话框。所有对话框都使用相同的线程来执行 WinForms 中的显示更新内容。事实上,它们甚至必须在同一个线程上运行。

有一个(有时可以接受)使用Application.DoEvents() 的hack,但我也不会使用它,因为它也会让你陷入很多问题。

所以简单的答案是:这行不通。使用后台工作人员进行冗长的处理。

【讨论】:

  • 我可能不会提到Application.DoEvents()(即使有警告),因为它正在宣传一种可以说是糟糕的 API 方法,新手最好不要知道。 :)
  • @MickyDuncan:不确定...我不会让新手发现他们自己的所有陷阱。在我的代码中摆脱它确实很糟糕:-/
  • 其实任何STA线程都可以显示UI、对话框等。实际的BackgroundWorker 线程不能是 STA 线程(它是线程池线程,.NET 无法控制其初始化),但“所有对话框都使用同一个线程”不是真的,它们也不是“必须在同一个线程上运行”。 我不建议每个进程使用多个 UI 线程。只是说每个进程不能有多个 UI 线程实际上是不正确的。
  • 我尝试使用后台工作人员来完成冗长的工作。然而,这会导致许多异常,可能是因为该方法使用了主线程中的 API 和方法。
  • @Kenny:您是否尝试从后台线程更新 UI(即设置控件属性)?如果是这样,您必须使用Invoke()
猜你喜欢
  • 2015-04-15
  • 1970-01-01
  • 2018-02-12
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 2010-10-06
  • 1970-01-01
  • 2010-12-16
相关资源
最近更新 更多