C#,如何在当前线程中访问在其他线程创建的内容?

3
我正在使用C#编写聊天客户端/服务器应用程序,但是线程出了问题。我编写了这个简单的代码来展示我的问题。
我使用thread_1来显示窗体,但它只显示了一秒钟(也许thread_1已经终止并关闭了窗体,但IsAlive显示它仍然存活!)。Thread_2尝试访问在主线程上创建的texBox,但它显示:
“跨线程操作无效:从其他线程访问控件'textBox2'。”
我不知道如何解决第一个问题,但我使用BackgroundWorker解决了第二个问题,但我想使用线程来完成。有什么办法吗?
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    Thread t1;
    Thread t2;


    private void button1_Click(object sender, EventArgs e)
    {

        t1 = new Thread(doThread1);
        t1.Name = "thread_1";

        t2 = new Thread(doThread2);
        t2.Name = "thread_2";

        t1.Start();
        t2.Start();

        MessageBox.Show(t1.IsAlive.ToString());
    }

    private void doThread1()
    {
        Form frm2 = new Form();
        frm2.Show();
    }


    private void doThread2()
    {
        try
        {
            for (int j = 10000; j > 0; j--)
                textBox.Text = j.ToString();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }


}

当doThread1结束时,该表单将被垃圾回收,无论它是一个单独的线程。 - Alxandr
尝试使用此线程从另一个线程向文本框写入内容 - CoolNLStuff
7
伙计,你这样做的方式不对。所有用户界面都应该保持在它自己的线程中。后台任务可以有任意数量的线程,但是后台任务不能且不应该访问用户界面。 - linkerro
@Exceptive,您链接的问题已被删除。 - Ral Zarek
1个回答

2
如Linkerro所说,您想将您调用的线程1取出,因为当您启动时,UI已经在一个线程中(所有程序都有一个单一的主线程)。您正在正确的轨道上,但是您希望将任何长时间运行的任务放在单独的线程上,以便它不会阻塞UI。唯一的技巧是您不能直接从后台线程操纵UI对象,它们必须从拥有它们的线程操作(这就是您收到的错误消息所说的)。
幸运的是,在.NET中有一种非常简单的方法可以实现此目的。在WPF中,您使用UiComponent.Dispatcher.Invoke(),而Winforms只需使用UiComponent.Invoke()。这使您的后台线程可以跨到UI组件所在的线程以更新它。
Invoke采用一个委托,该委托表示您想在UI线程上运行的操作。在我的示例中,我传递了一个通过使用lambada表达式初始化的动作,不需要参数并且不返回值。
请尝试此操作。
private void doThread2()
{
    try
    {
        for (int j = 10000; j > 0; j--)
        {
            textBox.Invoke(new Action(() =>
                textBox.Text = j.ToString()));
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

这里是使用任务的完整示例,演示了如何实现此功能。在计数时,您可以自由地移动窗口,而不会出现卡顿的情况。但是,如果删除任务并保留循环,则会发现窗口会冻结,因为循环将阻塞UI线程。
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Task.Factory.StartNew(() =>
        {
            for (int x = 0; x < 100000000; x++)
            {
                label1.Invoke(new Action(() =>
                    label1.Text = x.ToString()));
            }
        });
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接