2012-04-25 79 views
1

我爲氣象類寫了這個。出於某種原因,對於大數量(大量光子),文本框不能正確更新GUI。計算完成,但文本框不會更新。調用不會適當地更新GUI

我懷疑問題是調用Invoke(),但我不能爲我的生活看到有什麼問題。我試過使用Invoke()和BeginInvoke()來得到類似的結果。

任何人都可以幫助找出我要去哪裏錯了嗎?

謝謝!

PS>請原諒全局變量。使用InvokeBeginInvoke更新UI打算在清理起來後...

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; 
    } 
} 

}

+0

好主意:)我試過了,但結果相同。 – Maxthecat 2012-04-25 22:15:25

+1

嘗試通過使用程序的簡化版本本地化一個問題只是爲了測試,刪除所有進度條相關的代碼,留下一個文本框,並每秒更新它,讓我們說'1)textBox.Invoke(); 2)Thread.Sleep(1000); 3)textBox.Invoke()'看看它是否工作,並把這裏簡化的例子 – sll 2012-04-25 22:17:23

+0

噢!只是注意到,狀態欄代碼被刪除,更新代碼按預期工作......我不知道是否因爲它多次調用更新欄的step函數(例如:如果你調用它1000000次,文本框的消息是丟失) – Maxthecat 2012-04-25 22:23:00

回答

3

停止。儘管你可能會被告知它並不是那麼棒的解決方案。實際上,在這樣的情況下,你所要做的就是用進度信息更新UI,這可能是最糟糕的解決方案。相反,讓您的工作線程將其進度信息發佈到可以與UI線程共享的不可變數據結構。然後使用System.Windows.Forms.Timer,讓UI線程在合理的時間間隔內進行輪詢。

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會發生的來自UI的響應。

我翻譯這些InvokeBeginInvoke解決方案很多,因爲在很多情況下,它們是非常糟糕的解決方案。使用BackgroundWorker稍微好一點,但它仍然會迫使您進入更新UI的推送方法(儘管如此,仍然通過幕後的相同編組技術)。拉方法可以(並且通常是)更優雅的解決方案,並且通常更高效。

+1

太棒了!我同意,強調。這是實現代碼更優雅的方式。我將來會使用這個模型。謝謝! :) – Maxthecat 2012-04-26 21:54:52