3

我正在用 c# 编写聊天客户端/服务器应用程序,但线程有问题。我写了这个简单的代码来显示我的问题。

我使用 thread_1 来显示 Form 但它只显示一秒钟(也许 thread_1 终止并关闭了 Form ,但我 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);
        }
    }


}
4

1 回答 1

2

正如 Linkerro 所提到的,你想取出你调用线程 1 的线程,因为你的 UI 在你启动时已经在一个线程中(所有程序都有一个它们启动的主线程)。不过,您走在正确的轨道上,您希望将任何长时间运行的任务放在单独的线程上,这样它就不会阻塞 UI。唯一的技巧是您不能直接从后台线程操作 UI 对象,它们必须从拥有它们的线程中操作(这是您收到的错误消息)。

幸运的是,在 .NET 中有一种非常简单的方法可以实现这一点。在 WPF 中,您使用 UiComponent.Dispatcher.Invoke(),而 Winforms 只使用 UiComponent.Invoke()。这允许您的后台线程跨步到 UI 组件所在的线程以更新它。

Invoke 接受一个代表您希望在 UI 线程上运行的操作的委托。在我的示例中,我传入了一个使用 lambda 表达式初始化的操作,不带参数也不返回任何值。

尝试这个

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

这是一个完整的示例,说明如何使用 Tasks 执行此操作。您会在计数时看到您可以自由移动窗口并且它不会锁定。但是取出任务并离开循环,您将看到窗口如何冻结,因为循环会阻塞 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()));
            }
        });
    }
}
于 2012-06-29T07:17:08.747 回答