2

I have a C# Windows app and there is a Button on Form1 that when pressed runs a long program. While the program is running I want the User Interface to be available so I will put most of the code into a separate thread. As a test, I put code into a thread and looked to see if it would have any problems. I have 2 problems. My ultimate desire is to get the UI to work so let me know if this is not the best way to start a new thread.

First, although the program compiled, the thread that I created does not see ALL of the values in the variables from the main thread. Most of the strings are empty and the int and float values are 0. The only variables that keep their values in the thread are those that are created with a value and then never changed. Obviously, I should be able to see all of the values in all of the variables.

Second, I have a Textbox on the form that I append text to so that I can give information on the long running program. The Textbox displays information from the main thread without a problem but nothing is displayed from the thread that I created. I want the textbox on Form1 to be updated from the thread also.

I am using Visual Studio 2008 on Windows XP.

These are the definitions of the variables. They are in the Program.cs portion of the app.

partial class Form1
{
    string    TBI_File = "";
    int   junk = 27;

    string junkstr = "Two out of three ain\'t bad";
    double RADD;
    string PROGRAMMER = "Don and Jim"; 
    float  currentSize = 8.25F;        
    float sizechange = 10.0F;  
}

In the main thread (after the Button is pressed) I create the new thread. I copied and modified this code from http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx I commented the Abort and Join because at this point of the testing I want the thread tro continue to run until I stop it separately.

     Wprintf("Alpha.Beta starting");
     Alpha oAlpha = new Alpha();

     // Create the thread object, passing in the Alpha.Beta method
     // via a ThreadStart delegate. This does not start the thread.
     Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));

     // Start the thread
     oThread.Start();

     // Spin for a while waiting for the started thread to become
     // alive:
     while (!oThread.IsAlive) ;

     // Put the Main thread to sleep for 1 millisecond to allow oThread
     // to do some work:
     //original
    //Thread.Sleep(1);
     Thread.Sleep(10);

     // Request that oThread be stopped
     //oThread.Abort();

   // Wait until oThread finishes. Join also has overloads
     // that take a millisecond interval or a TimeSpan object.
     //oThread.Join();

     Wprintf("Alpha.Beta has finished");

Below is the code that is run by the thread.

public class Alpha : Form1
{

    // This method that will be called when the thread is started
    public void Beta()
    {
        while (true)
        {
            //Console.WriteLine("Alpha.Beta is running in its own thread.");
            Wprintf("Alpha.Beta is running in its own thread. " + 
                " RADD: " + RADD + 
                " CurrentSize: " + currentSize.ToString() +
                " TBI_File: " + TBI_File +
                " PROGRAMMER: " + PROGRAMMER +
                " sizechange: " + sizechange.ToString() +
                " junk: " + junk +
                " junkstr: " + junkstr);

           textBox1.AppendText("Alpha.Beta is running in its own thread.");

        }
    }
};

Wprintf appends that message to a log file and adds the messges to the Textbox. It works for the entire program except that appending to the end of the textbox does not work from the thread created. I added the TextBox1.AppendText above (which is in the thread) to try to get that to work but it does not do anything and no message is displayedin the textbox from the thread.

The portion of the log file is below. The log file is appended from the thread so I can see what the values of the variables are in the thread(I also looked at the variables from the debugger and got the same values) Variables changed are RADD and TBI_FILE and you can see below that RADD is 0.0 and TBI_File is ‘’ in the thread. The others are not changed in the program and just got the value that was set when it was declared.

 Alpha.Beta is running in its own thread. RADD: 0  CurrentSize: 8.25 TBI_File:  PROGRAMMER: Don and Jim   sizechange: 10 junk: 27   junkstr: Two out of three ain't bad

I asked about an earlier version of this question here: Initial Form of C# program unavailable while the program is running

As I indicated previously, I need to have the UI (Textbox and clicking the X for exit) to be available so let me know if this is not a good way to do it.

Thanks,

4

3 に答える 3

5

考えていただきたいことがいくつかあります。

最も重要なことは、バックグラウンド スレッドから UI を変更できないことです。コントロールを変更できるのは UI スレッドだけです。だからあなたtextBox1.AppendTextはうまくいきません。次のように、Invoke を呼び出して UI スレッドと同期する必要があります。

this.Invoke((MethodInvoker) delegate
    {
        textBox1.Append("Alpha.Beta is running in its own thread.");
    });

もちろん、UI スレッドはバックグラウンド スレッドが完了するのを待っているため、UI は更新されません。

独自のスレッドの管理に苦労することもありますが、面倒な詳細のほとんどを処理するBackgroundWorkerを使用する方がよいでしょう。

// set up the worker
BackgroundWorker worker = new BackgroundWorker();
worker.ReportsProgress = true;
worker.DoWork = worker_DoWork;  // method that's called when the worker starts

// method called to report progress
worker.ProgressChanged = worker_progressChanged;

// method called when the worker is done
worker.RunWorkerCompleted = worker_workCompleted;

// start worker
worker.RunWorkerAsync();


void worker_DoWork(object sender, DoWorkEventArgs e)
{
        //Console.WriteLine("Alpha.Beta is running in its own thread.");
        Wprintf("Alpha.Beta is running in its own thread. " + 
            " RADD: " + RADD + 
            " CurrentSize: " + currentSize.ToString() +
            " TBI_File: " + TBI_File +
            " PROGRAMMER: " + PROGRAMMER +
            " sizechange: " + sizechange.ToString() +
            " junk: " + junk +
            " junkstr: " + junkstr);
    worker.ReportProgress(0, "Alpha.Beta is running in its own thread.");
}

void worker_progressChanged(object sender, ProgressChangedEventArgs e)
{
    textBox1.Append((string)e.UserState);
}

void worker_workCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    textBox1.Append("Worker done!");
}

UI スレッドはワーカーを開始し、そのまま続行します。ワーカーが完了するまで待たせないでください。ワーカーが完了したときに通知を受け取りたい場合は、RunWorkerCompletedイベントを処理できます。または、ワーカーが完了したかどうかをポーリングするだけの場合は、定期的にIsBusyプロパティをチェックするタイマーを設定できます。ただし、 を使用したほうがよいでしょうRunWorkerCompleted

バックグラウンド スレッドが完了するまでUI スレッドを待機させないでください。もしそうなら、バックグラウンド スレッドを持つ意味は何ですか?

于 2013-07-23T19:37:15.780 に答える
2

Form のインスタンスで値を設定します。次に、Form から継承する Alpha のインスタンスを作成し、Form が行う初期化も実行します。フォームで行われたその他の変更は、他のインスタンスには表示されません (静的変数を除く)。Alpha インスタンスを更新するか、同じインスタンスを使用する必要があります。

メイン スレッド以外のスレッドからコントロールにアクセスすることはできません。これを行う適切な方法は、Control.Invoke を使用することです。たとえば、 Thread Control.Invokeを参照してください。

また、このまま (しばらく) 待機すると、メイン スレッドが動かなくなります。何かが完了するのを待ちたい場合は、イベントを処理する必要があります (ワーカー スレッドが終了したことを通知するか、backgroundworker として work_completed イベントに登録します)。

その他 - Form から継承してもよろしいですか? これは本当に必要ですか?

于 2013-07-23T19:48:10.313 に答える