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

标签 c# multithreading

我有一个 C# Windows 应用程序,在 Form1 上有一个按钮,当按下它时会运行一个长程序。当程序运行时,我希望用户界面可用,因此我会将大部分代码放入一个单独的线程中。作为测试,我将代码放入一个线程中并查看它是否有任何问题。我有两个问题。我的最终愿望是让 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;  
}

在主线程中(按下按钮后)我创建了新线程。我从 http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx 复制并修改了这段代码我对 Abort 和 Join 进行了评论,因为在测试的这一点上,我希望线程继续运行,直到我单独停止它。

     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

我在这里询问了这个问题的早期版本:Initial Form of C# program unavailable while the program is running

正如我之前指出的,我需要让 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 线程启动 worker,然后继续。不要让它等待 worker 完成。如果您想在工作人员完成时收到通知,您可以处理 RunWorkerCompleted 事件。或者,如果您只想轮询以查看工作人员是否已完成,您可以让计时器定期检查 IsBusy 属性。不过,您最好使用 RunWorkerCompleted

永远不要让您的 UI 线程等待后台线程完成。如果这样做,那么拥有后台线程有什么意义?

关于c# - 在 C# 中让线程从主线程获取变量值的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17819158/

相关文章:

c# - 使用mathdotnet求解线性方程组?

c++ - 将两个线程同步到同一个定时器

Java多线程示例-对象是否共享

c# - 扩展至完整模式时更改 WP8 ListPicker 背景颜色

c# - MySql Server中将两个表的数据写入一个xml文件中

c# - RSA 加密返回不同的输出

c# - 如何使用文件关联执行已启动应用程序的事件?

c++ - 对共享数据的线程安全访问 - 读/写实际上发生并且不会发生重新排序

java - 中断java中正常运行的线程

c++ - 在处理程序中排队请求时,从多个线程或一个线程调用boost asio io_service.run