【问题标题】:Invoke freeze my Windows form调用冻结我的 Windows 窗体
【发布时间】:2015-12-21 18:46:19
【问题描述】:

我正在尝试做我的第一个多线程窗口表单应用程序,但我遇到了一个重要问题。 我知道我必须在我的线程中使用 Invoke 或 BeginInvoke 来更改我的表单中的 RichTextBox,但是当我尝试这样做时我的 UI 只是冻结,而控制台正在正确打印我的结果。

     class MyThread
    {
        public MyThread()
        {

        }


        public void Run()
        {
            byte[] tmp;
            while (true)
            {

                // lock (sem1)
                Monitor.Enter(sem1);
                {
                    if (end)
                    {
                        Monitor.Exit(sem1);
                        break;
                    }
                        tmp = inp.ReadNr();

                    if (inp.End())
                        end = true;
                }
                if(inOrder)
                Monitor.Enter(sem2);
                Monitor.Exit(sem1);

                bool p = CSL.checkNr((byte[])tmp.Clone());
                Monitor.Enter(sem3);
                if(inOrder)
                Monitor.Exit(sem2);
                {
                    outp.WriteNr(tmp, p);
                    //win.richTextBox2.AppendText(String.Join("", tmp) + '\n');
                    win.richTextBox2.BeginInvoke(new Action(delegate ()
                    {
                        win.richTextBox2.AppendText("wtf");
                    }));
                }
                Monitor.Exit(sem3);

            }
        }
    }

“win”是我的 Form1 类的对象。我受不了了……

编辑:

    static public void loading()
    {
        outp = new oFile();
        Thread[] thr = new Thread[nrThreads];
        for (int a = 0; a < nrThreads; a++)
            new Thread(new ThreadStart(new MyThread().Run)).Start();
        //new MyThread().Run();
        Console.ReadKey();
    }

表格:

private void button1_Click(object sender, EventArgs e)
    {
        Program.loading();
    }

【问题讨论】:

  • Run 方法是在后台线程还是 UI 线程上运行?
  • 附带说明,直接使用Monitor.EnterMonitor.Exit 而不是lock 语句是不寻常的,并且可能是个坏主意,除非您真的知道自己在做什么。例如,如果您在EnterExit 之间的代码抛出异常,则该对象将处于锁定状态。
  • @adv12 我刚刚编辑了我的帖子我是如何运行我的线程的。 UI 线程正在运行方法,它正在创建和启动我的线程。
  • Program.loading() 是做什么的?
  • 基本上你所做的是停止为拥有表单的线程处理消息的消息循环。 BeginInvoke 通过向该线程发布消息来工作,该消息最终由该消息循环处理。当您执行 Console.ReadKey 时,一切都停止工作,与线程或 begininvoke 无关,尽管它们是相同问题的受害者。此外,您应该将 async/await 视为完全多线程编程的替代方案,它有些不同,而且比较容易。

标签: c# multithreading winforms invoke begininvoke


【解决方案1】:

这里的问题是Program.loading中的这一行:

Console.ReadKey();

这会停止主 GUI 线程,让我们看看现在发生了什么:

  1. 发布到主 GUI 线程的消息不会得到处理(因为您没有返回到消息循环)
  2. 来自不同线程的 BeginInvoke 不会得到处理,因为它们通过将消息发布到主 GUI 线程正在处理(现已停止)的同一消息队列来工作
  3. 直到您按下一个键(假设这是可能的),一切都会停止。您会注意到向表单发送的消息不会得到处理,最终表单会淡出并显示“已停止响应”文本。

所以删除那行代码,至少你的程序不应该像以前那样停在它的轨道上。


话虽如此,我还是敦促您从尝试更简单的示例开始。您对多台显示器的交错手动处理让我觉得您刚刚开始学习多线程编程,并且已经开始使用一些需要非常准确地互锁才能正常工作的复杂齿轮。

相反,我希望您查看async / await,这是 C# 5 中添加的新关键字,用于处理异步编程。尽管异步和多线程不是一回事,也没有暗示对方,但异步编程通常可以让您在大部分情况下获得响应式 UI,在等待时不会冻结。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多