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

Invoke使用を中止しBeginInvokeて 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 スレッドは、コントロールを更新するタイミングと頻度を決定します。
  • の場合のように UI からの応答を待つ必要がないため、ワーカー スレッドのスループットが向上しますInvoke

多くの場合、それらはひどい解決策であるため、私はこれらInvokeと解決策を大いに食い物にしています。BeginInvokeを使用するBackgroundWorker方が少し良いですが、UI を更新するプッシュ方式を強制する必要があります (それでも、舞台裏で同じマーシャリング手法を使用します)。pull メソッドは (多くの場合) より洗練されたソリューションであり、通常はより効率的です。

于 2012-04-26T14:49:54.423 に答える