C#,多线程-表单未更新

约翰

我有一个跨多个线程运行的蒙特卡洛模拟程序,并带有一个进度条,以告知用户进展情况。使用Invoke在单独的线程中完成进度条管理,但是该表单未更新。

这是我的代码:

Thread reportingThread = new Thread(() => UpdateProgress(iSims, ref myBag));
reportingThread.Priority = ThreadPriority.AboveNormal;
reportingThread.Start();`

这是被调用的函数:

private void UpdateProgress(int iSims, ref ConcurrentBag<simResult> myBag)
{
    int iCount;
    string sText;

    if (myBag == null)
        iCount = 0;
    else
        iCount = myBag.Count;

    while (iCount < iSims)
    {
        if (this.Msg.InvokeRequired)
        {
             sText = iCount.ToString() + " simultions of " + iSims + " completed.";
            this.Msg.BeginInvoke((MethodInvoker) delegate() { this.Msg.Text = sText; this.Refresh(); });
        }
       Thread.Sleep(1000);
       iCount = myBag.Count;
    }
}

我已经使用了Application.DoEvents()和this.refresh()来尝试强制更新表单,但是什么也没有发生。

更新:这是调用上述函数的过程

private void ProcessLeases(Boolean bValuePremium)
    {
        int iSims, iNumMonths, iNumYears, iIndex, iNumCores, iSimRef;
        int iNumSimsPerThread, iThread, iAssets, iPriorityLevel;
        string sMsg;
        DateTime dtStart, dtEnd;
        TimeSpan span;
        var threads = new List<Thread>();
        ConcurrentBag<simResult> myBag = new ConcurrentBag<simResult>();
        ConcurrentBag<summaryResult> summBag = new ConcurrentBag<summaryResult>();

        this.Msg.Text = "Updating all settings";
        Application.DoEvents();
        ShowProgressPanel();

        iSims = objSettings.getSimulations();
        iNumCores = Environment.ProcessorCount;

        this.Msg.Text = "Initialising model";
        Application.DoEvents();
        iNumSimsPerThread = Convert.ToInt16(Math.Round(Convert.ToDouble(iSims) / Convert.ToDouble(iNumCores), 0));

        this.Msg.Text = "Spawning " + iNumCores.ToString() + " threads";

        for (iThread = 0; iThread < iNumCores; iThread++)
        {
            int iStart, iEnd;

            if (iThread == 0)
            {
                iStart = (iThread * iNumSimsPerThread) + 1;
                iEnd = ((iThread + 1) * iNumSimsPerThread);
            }
            else
            {
                if (iThread < (iNumCores - 1))
                {
                    iStart = (iThread * iNumSimsPerThread) + 1;
                    iEnd = ((iThread + 1) * iNumSimsPerThread);
                }
                else
                {
                    iStart = (iThread * iNumSimsPerThread) + 1;
                    iEnd = iSims;
                }
            }
            Thread thread = new Thread(() => ProcessParallelMonteCarloTasks(iStart, iEnd, iNumMonths, iSimRef, iSims, ref objDB, iIndex, ref objSettings, ref myBag, ref summBag));
            switch (iPriorityLevel)
            {
                case 1: thread.Priority = ThreadPriority.Highest; break;
                case 2: thread.Priority = ThreadPriority.AboveNormal; break;
                default: thread.Priority = ThreadPriority.Normal; break;
            }
            thread.Start();
            threads.Add(thread);
        }

        // Now start the thread to aggregate the MC results
        Thread MCThread = new Thread(() => objPortfolio.MCAggregateThread(ref summBag, (iSims * iAssets), iNumMonths));
        MCThread.Priority = ThreadPriority.AboveNormal;
        MCThread.Start();
        threads.Add(MCThread);

        // Here we review the CollectionBag size to report progress to the user
        Thread reportingThread = new Thread(() => UpdateProgress(iSims, ref myBag));
        reportingThread.Priority = ThreadPriority.AboveNormal;
        reportingThread.Start();

        // Wait for all threads to complete
        //this.Msg.Text = iNumCores.ToString() + " Threads running.";
        foreach (var thread in threads)
            thread.Join();

        reportingThread.Abort();

        this.Msg.Text = "Aggregating results";
        Application.DoEvents();

        this.Msg.Text = "Preparing Results";
        Application.DoEvents();
        ShowResults();
        ShowResultsPanel();
    }

如您所见,在调用调用之前,对Form进行了许多更新,并且都可以正常工作-在每种情况下,我都使用Application.DoEvents()进行更新。

myBag是一个ConcurrentBag,每个monte-carlo线程都将其结果转储到其中。通过使用Count方法,我可以看到完成了多少次仿真并更新了用户。

J ...
foreach (var thread in threads)
    thread.Join();

这是你的问题。您正在此处进行阻止,因此在所有线程完成之前,UI线程中将不会更新任何内容。

这是一个关键点-.DoEvents()每当您附加到用户界面事件处理程序的代码块完成执行时,自然就自然发生,并且全部发生。作为开发人员的主要职责之一是确保在用户界面事件处理程序中执行的所有代码均及时完成(最多几百毫秒)。如果您以这种方式编写代码,则永远都不需要调用DoEvents()...。

您应该始终以这种方式编写代码。

除了性能优势外,使用线程的主要优点是它们本质上是异步的-要利用此线程,您必须相应地编写代码。打破程序习惯是很难的。您需要做的是.Join完全忘记ProcessLeases在这里摆脱方法-让UI再次拥有控制权。

您已经在处理线程中的更新,因此,您所要做的就是完成通知,以便当所有线程完成其工作时可以在新的处理程序中接听。您需要跟踪线程-让它们每个在完成时通知(即:在UI线程上调用一些委托,等等),并且在处理它的任何方法中您都将执行以下操作

 if (allThreadsAreFinished)  // <-- You'll need to implement something here
 {
      reportingThread.Abort();
      this.Msg.Text = "Preparing Results";         
      ShowResults();
      ShowResultsPanel();
 }

另外,您也可以简单地调用ProcessLeases一个后台线程(确保正确调用其中的所有调用),然后再用来阻塞该线程也没关系.Join然后,您还可以消除对的所有混乱调用.DoEvents()

此外,您不需要致电到this.Refresh();这里:

 this.Msg.BeginInvoke((MethodInvoker) delegate() { 
      this.Msg.Text = sText; 
      this.Refresh(); 
 });

如果您阻止UI线程,则没有该控件,控件将进行更新,并且您只会添加任何额外的工作。如果你正在阻塞UI线程然后添加.Refresh()调用不会帮助,因为UI线程不会随意执行任何比它可以自由地执行前行。这是无序的编程-随机添加代码以希望它能正常工作,而不是检查和理解不起作用的原因。


第2章:工作场所类比。

想象一下,UI线程就像管理器一样。管理员可以通过多种方式委派任务。使用.Join完毕后,有点像经理给每个人要做的工作-乔得到一份工作,露西得到另一份工作,比尔得到第三份,萨拉得到第四份。经理有跟进工作,一旦每个人都完成了,所以他提出了一个计划,以尽快完成它。

在给每个人完成任务之后,经理立即走到乔坐在办公桌前,凝视着他,什么也没做,直到乔完成为止。乔完成工作后,他移到露西的书桌上检查她是否完成了工作。如果不是她,他会等到露西说完,然后移到比尔的桌子上,凝视着他,直到他做完为止……然后移到萨拉的桌子上。

显然,这没有效果。此外,四名团队成员中的每一个都已向其Manager.BeginInvoke -> read your email!经理发送电子邮件状态更新(),但他没有阅读任何信息,因为他一直将所有时间都花在坐在他们的办公桌旁,盯着他们,等待他们完成他们的任务。他也没有做任何其他事情。老板一直在问这是怎么回事,他的电话一直在响,没有人更新每周的财务状况-什么都没有。经理无法做任何其他事情,因为他认为他需要坐在自己的屁股上,看着他的团队工作直到完成为止。

管理员没有回应...如果您等待,管理员可能会再次回应。你想解雇经理吗?

[YES - FIRE HIM] [NO - Keep Waiting]

更好的是,人们会想到,如果经理简单地让每个人都去从事某些工作,然后继续做其他事情。他唯一关心的是他们何时完成工作,因此,他们要做的又是一条指令,以便他们在工作完成时通知他。UI线程就像您的应用程序的管理器一样-它的时间很宝贵,您应该在绝对必要的情况下使用尽可能少的时间。如果您有工作要做,请委派给工作线程,不要让经理坐在那里等别人完成工作-让他们在事情准备就绪时通知他们,然后让经理恢复工作。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章