在C#中让线程从主线程获取变量值的问题

Jim*_*Jim 2 c# multithreading

我有一个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;  
}
Run Code Online (Sandbox Code Playgroud)

在主线程中(按下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");
Run Code Online (Sandbox Code Playgroud)

下面是该线程运行的代码.

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

        }
    }
};
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我在这里询问了这个问题的早期版本:程序运行时C#程序的初始形式不可用

正如我之前所说,我需要有UI(文本框并单击X退出),所以如果这不是一个好方法,请告诉我.

谢谢,

Jim*_*hel 5

你想要考虑几件事.

最重要的是,您无法从后台线程修改UI.只有UI线程可以修改控件.所以你textBox1.AppendText不会工作.您需要调用Invoke以与UI线程同步,如下所示:

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

当然,这不会更新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!");
}
Run Code Online (Sandbox Code Playgroud)

您的UI线程启动工作程序,然后继续.不要让它等待工人完成.如果您希望在工作完成时收到通知,则可以处理该RunWorkerCompleted事件.或者,如果您只想轮询以查看工作人员是否已完成,您可以定期检查该IsBusy属性.不过,你最好RunWorkerCompleted还是这样.

不要永远做你的UI线程等待后台线程来完成.如果你这样做,有什么背景线程的意义?