簡體   English   中英

在C#中讓線程從主線程獲取變量值的問題

[英]Problems getting a thread to have variable values from main thread in C#

我有一個C#Windows應用程序,並且在Form1上有一個按鈕,當按下它時會運行一個長程序。 程序運行時,我希望用戶界面可用,所以我將大部分代碼放入一個單獨的線程中。 作為測試,我將代碼放入一個線程中,看看它是否會有任何問題。 我有2個問題。 我的最終願望是讓UI工作,所以讓我知道這不是開始新線程的最佳方式。

首先,雖然編譯了程序,但我創建的線程沒有看到主線程變量中的所有值。 大多數字符串都是空的,int和float值為0.在線程中保留其值的唯一變量是使用值創建然后從未更改的變量。 顯然,我應該能夠看到所有變量中的所有值。

其次,我在表單上有一個文本框,我附加文本,以便我可以提供有關長時間運行程序的信息。 文本框顯示主線程中的信息沒有問題,但我創建的線程沒有顯示任何內容。 我希望Form1上的文本框也可以從線程更新。

我在Windows XP上使用Visual Studio 2008。

這些是變量的定義。 它們位於應用程序的Program.cs部分。

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

在主線程中(按下Button后),我創建了新線程。 我復制並修改了這段代碼來自http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx我評論了Abort和Join,因為在測試的這一點我想要線程tro繼續跑,直到我分開停止。

     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");

下面是該線程運行的代碼。

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將該消息附加到日志文件,並將消息添加到文本框中。 它適用於整個程序,除了附加到文本框的末尾不能從創建的線程起作用。 我在上面添加了TextBox1.AppendText(在線程中)以嘗試使其工作,但它沒有做任何事情,並且在線程的文本框中沒有顯示消息。

日志文件的部分如下所示。 日志文件是從線程中追加的,所以我可以看到變量的值在線程中(我還查看了調試器中的變量並得到了相同的值)變量變量是RADD和TBI_FILE,你可以在下面看到RADD是0.0,TBI_File在線程中是''。 其他程序在程序中沒有更改,只是獲得了聲明時設置的值。

 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

我在這里詢問了這個問題的早期版本: 程序運行時C#程序的初始形式不可用

正如我之前所說,我需要有UI(文本框並單擊X退出),所以如果這不是一個好方法,請告訴我。

謝謝,

你想要考慮幾件事。

最重要的是,您無法從后台線程修改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線程等待后台線程來完成。 如果你這樣做,有什么背景線程的意義?

您在Form的實例中設置值。 然后,您創建一個繼承自Form的Alpha實例,從而執行Form所做的初始化。 在Form中進行的其他更改將不會在其他實例中顯示(不包括靜態變量)。 您應該更新Alpha實例或使用相同的實例。

除了主要線程之外,您無法從任何線程訪問控件。 正確的方法是使用Control.Invoke,例如參見Thread Control.Invoke

此外,等待你的方式(一段時間)將使主線卡住。 如果您希望等待某些事情完成 - 您將不得不處理事件(讓工作線程信號完成,或者您是背景工作者並注冊work_completed事件)。

還有別的 - 你確定要繼承Form嗎? 這真的有必要嗎?

首先,讓我給出一些針對這種情況的個人建議:1)在這種情況下,我寧願使用BackgroundWorker而不是原始的Thread類。 2)線程(無論何種類)都不能直接與UI或其他線程通信,或者至少它們不應該。

- 讓我們回答:由於Threads不能/不應該直接訪問你的主線程變量,你必須將它們作為參數傳遞給BackgroundWorker。

以下是一個不言自明的代碼,如果您還有任何疑問,請在下面評論。

    private BackgroundWorker worker = new BackgroundWorker();
    public Form1()
    {
        InitializeComponent();

        //Register the event/handlers for: Do Work, ProgressChanged, and Worker Completed.
        worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
        worker.WorkerReportsProgress = true; //Let's tell the worker that it WILL be ABLE to report progress.
        worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); //Method that will be called when the progress has been changed.            
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); //Method that will be called when the thread finish executing.

        //Start the thread async.
        worker.RunWorkerAsync();
    }

    /// <summary>
    /// Method that will run in a new thread async from the main thread.
    /// </summary>        
    /// <param name="e">Arguments that are passed to the Worker Thread (a file, path, or whatever)</param>
    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        //Get the argument. In this example I'm passing a pathFile.
        string pathFile = (string)e.Argument;

        for (int i = 0; i <= 100; i+=10) //For demonstration purposes we're running from 0 to 99;
        {
            System.Threading.Thread.Sleep(100); //Sleep for demonstration purposes.

            //I want to update the Log, so the user will be notified everytime
            //the log is updated through the ReportProgress event.
            string myLog = i + " ";

            //Invoke the event to report progress, passing as parameter the
            //percentage (i) and the current log the thread has modified.
            worker.ReportProgress(i, myLog);
        }            

        e.Result = "I've made it!!!!! - My complex cientific calculation from NASA is 654.123.Kamehameha)";
    }        

    /// <summary>
    /// Invoked when the worker calls the ReportProgress method.
    /// </summary>        
    /// <param name="e">The arguments that were passed throgh the ReportProgress method</param>
    private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        //Get the Percentage and update the progressbar.
        progressBar1.Value = e.ProgressPercentage;

        //Get the EventArgs and respectively the new log that the thread has modified and append it to the textbox.
        textBox1.AppendText((string)e.UserState);
    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Lets check whether the worker has runned successfully (without cancelling and without any errors)
        if (!e.Cancelled && e.Error == null)
        {
            //Lets display the result (Result is an object, so it can return an entire class or any type of data)
            MessageBox.Show((string)e.Result);
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM