1

我是为一堂气象课写的。出于某种原因,GUI 上的文本框没有针对大量(大量光子)正确更新。计算完成,但文本框不更新。

我怀疑问题出在调用 Invoke() 上,但我一辈子都看不到出了什么问题。我尝试过使用 Invoke() 和 BeginInvoke() 并获得类似的结果。

任何人都可以帮助找出我要去哪里错了吗?

谢谢!

PS> 请原谅全局变量。本来打算以后清理的。。。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace CloudTransmittance
{
public partial class Form1 : Form
{

    public Form1()
    {
        InitializeComponent();
    }


    private void buttonCalculate_Click(object sender, EventArgs e)
    {

        Thread t = new Thread(calculateModel);
        t.Start();


    }

    //number of photons that have gone to albedo, direct, or diffuse transmittance
    private ulong top = 0;
    private ulong direct = 0;
    private ulong diffuse = 0;
    private ulong absorbed = 0;
    private ulong failed = 0;
    private ulong photons = 0;


    private void calculateModel()
    {
        //model variables
        double theta = 0;
        double tauStar = 0;
        double omega = 0;
        double g = 0;
        photons = 0;

        //Get data from form
        theta = Convert.ToDouble(textBoxTheta.Text);
        tauStar = Convert.ToDouble(textBoxTau.Text);
        omega = Convert.ToDouble(textBoxOmega.Text);
        g = Convert.ToDouble(textBoxG.Text);
        photons = Convert.ToUInt64(textBoxPhotons.Text);

        //Clear the progress bar and set its limits

        this.progressBar1.BeginInvoke(
            (MethodInvoker)delegate()
            {
                this.progressBar1.Minimum = 0;
                this.progressBar1.Value = 0;
                this.progressBar1.Maximum = (int)photons;
                this.progressBar1.Step = 1;
            });

        //Clear the text boxes
        this.textBoxAlbedo.Invoke(
            (MethodInvoker)delegate()
             {
                  this.textBoxAlbedo.Text = "";
             });
        this.textBoxDirect.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBoxDirect.Text = "";
           });
        this.textBoxDiffuse.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBoxDiffuse.Text = "";
           });
        this.textBox1.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBox1.Text = "";
           });
        this.textBox2.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBox2.Text = "";
           });

        //convert theta to radians from degrees
        theta *= Math.PI / 180;

        //number of photons that have gone to albedo, direct, or diffuse transmittance
        top = 0;
        direct = 0;
        diffuse = 0;
        absorbed = 0;
        failed = 0;

        //Random number generator
        Random r = new Random();
        double randomValue = 0;

        int count = 1000; //number of iterations of the problem...
        double delta = 0.00001; //close enough to "1" for calculations, since C# random goes from [0, 1) instead of [0, 1]
        //Calculate transmittance

        for (ulong photonCount = 0; photonCount < photons; photonCount++)
        {

            bool scattered = false;
            double newTheta = theta; //needed for looping
            int i = 0; //counting variable used to prevent infinite looping
            for (i = 0; i < count; i++)
            {
                double length = calculateTauP(); //length of the photon's travel
                double newTau = calculateTau(newTheta, length);
                if (newTau < 0)
                {
                    top++; //photon has exited through the top
                    break; //move to the next photon
                }
                else if (newTau > tauStar)
                {
                    //exited through the bottom of the cloud
                    if (scattered == false)
                    {
                        //direct transmittance
                        direct++;
                    }
                    else
                    {
                        //diffuse transmittance
                        diffuse++;
                    }
                    break;
                }
                else
                {
                    //photon is either scattered or absorbed
                    randomValue = r.NextDouble();
                    if (randomValue >= omega)  // || ((omega == 1) && (randomValue >= (omega - delta)) )
                    {
                        //photon absorbed, no longer of interest
                        absorbed++;
                        break;
                    }
                    else
                    {
                        //photon scattered, determine direction
                        scattered = true;
                        newTheta = calculateNewAngle(newTau, newTheta, g, randomValue);
                    }
                }
            }
            if (i >= count)
            {
                failed++;
            }
            this.progressBar1.BeginInvoke(
            (MethodInvoker)delegate()
            {
                this.progressBar1.PerformStep();
            });
        }


        //Update Form values
        displayData();
    }

    private void displayData()
    {
        if (this.textBoxAlbedo.InvokeRequired)
        {
            this.textBoxAlbedo.Invoke(
            (MethodInvoker)delegate()
            {
                this.textBoxAlbedo.Text = ((double)top / (double)photons).ToString();
            });
        }
        else
        {
            textBoxAlbedo.Text = ((double)top / (double)photons).ToString();
        }
        if (this.textBoxDirect.InvokeRequired)
        {
            this.textBoxDirect.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBoxDirect.Text = ((double)direct / (double)photons).ToString();
               });
        }
        else
        {
            textBoxDirect.Text = ((double)direct / (double)photons).ToString();
        }
        if (this.textBoxDiffuse.InvokeRequired)
        {
            this.textBoxDiffuse.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBoxDiffuse.Text = ((double)diffuse / (double)photons).ToString();
               });
        }
        else
        {
            textBoxDiffuse.Text = ((double)diffuse / (double)photons).ToString();
        }
        if (this.textBox1.InvokeRequired)
        {
            this.textBox1.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBox1.Text = absorbed.ToString();
               });
        }
        else
        {
            textBox1.Text = absorbed.ToString();
        }
        if (this.textBox2.InvokeRequired)
        {
            this.textBox2.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBox2.Text = failed.ToString();
               });
        }
        else
        {
            textBox2.Text = failed.ToString();
        }

    }



    private double calculateNewAngle(double length, double angle, double g, double randomNumber)
    {
        double newAngle = 0;
        double cos = (1 / (2 * g)) * (1 + Math.Pow(g, 2) - Math.Pow(((1 - Math.Pow(g, 2)) / (1 + g * (2 * randomNumber - 1))), 2));
        newAngle += angle + cos;
        while (newAngle >= 2 * Math.PI)
        {
            newAngle -= 2 * Math.PI; //normalize the angle to 0 <= angle < 2PI
        }


        return newAngle;
    }

    private double calculateTauP()
    {
        Random r = new Random();
        double distance = -1 * Math.Log(1 - r.NextDouble());
        return distance;
    }

    private double calculateTau(double angle, double tauP)
    {
        double tau = tauP * Math.Cos(Math.PI/2 - angle);
        return tau;
    }
}

}

4

1 回答 1

4

停止使用InvokeBeginInvoke更新 UI。尽管您可能被告知,但这并不是一个很好的解决方案。实际上,在这种情况下,您只想用进度信息更新 UI,这可能是最糟糕的解决方案。相反,让您的工作线程将其进度信息发布到可以与 UI 线程共享的不可变数据结构。然后让您的 UI 线程使用System.Windows.Forms.Timer.

public class YourForm : Form
{

  private class ProgressInfo
  {
    public ProgressInfo(ulong top, ulong direct, ulong diffuse, ulong absorbed, ulong failed, ulong photons)
    {
      // Set properties here.
    }

    public ulong Top { get; private set; }
    public ulong Direct { get; private set; }
    public ulong Diffuse { get; private set; }
    public ulong Dbsorbed { get; private set; }
    public ulong Failed { get; private set; }
    public ulong Photons { get; private set; }
  }

  private volatile ProgressInfo progress = null;

  private void calculateModel()
  {
    for (ulong photonCount = 0; photonCount < photons; photonCount++)
    {
      // Do your calculations here.

      // Publish new progress information.
      progress = new ProgressInfo(/* ... */);
    }
  }

  private void UpdateTimer_Tick(object sender, EventArgs args)
  {
    // Get a local reference to the data structure.
    // This is all that is needed since ProgressInfo is immutable
    // and the member was marked as volatile.
    ProgressInfo local = progress;
    this.textBoxAlbedo.Text = ((double)local.Top / (double)local.Photons).ToString();
    this.textBoxDirect.Text = ((double)local.Direct / (double)local.Photons).ToString();
    this.textBoxDiffuse.Text = ((double)local.Diffuse / (double)local.Photons).ToString();
    this.textBox1.Text = local.Absorbed.ToString();
    this.textBox2.Text = local.Failed.ToString();
  }

注意这里的几件事。

  • 代码更容易理解和遵循。
  • UI 线程决定何时以及多久更新一次它的控件。
  • 您可以在工作线程上获得更多吞吐量,因为它不必像Invoke.

我经常使用这些InvokeBeginInvoke解决方案,因为在许多情况下它们是糟糕的解决方案。使用 aBackgroundWorker稍微好一点,但它仍然迫使您使用更新 UI 的推送方法(尽管如此,通过幕后的相同编组技术)。pull 方法可以(并且通常是)更优雅的解决方案,并且通常更有效。

于 2012-04-26T14:49:54.423 回答