【问题标题】:Having some issues with updating my GUI更新我的 GUI 时遇到一些问题
【发布时间】:2014-02-21 07:51:05
【问题描述】:

我正在制作一个 Hangman Windows 窗体应用程序,到目前为止它运行良好,逻辑在那里,但我在 GUI 同步方面遇到了一个较小的问题。

现在我正在显示来自 GameForm.cs 的 UI,它反过来在单独的线程上实例化一个名为 GameRound 的类,现在问题有点奇怪,gameround 循环有效,问题是,它没有t 在调用它们时更新 UI 元素(在这种情况下,一个显示刽子手的图片框、一个显示模糊单词的文本框和一个显示已经猜到的字符的标签),而是在每次循环迭代后更新它们。但是我注意到,如果我在循环中插入一个MessageBox.Show(),那么它们将在消息框被调用时被更新。

我猜这是某种线程同步问题,表单不想在 Gameround 线程工作时更新,但我是一个完整的初学者程序员,所以我需要一些帮助!

这是 Gameround(Gameround.Start()) 的代码

    public void Start()
    {
        Hangman hangman = new Hangman();
        do
        {
            //Check if hangman is dead first
            if (hangman.HangmanStep == 6) {
                MessageBox.Show("You got hanged! RIP");
                complete = true; }

            bool w_reduceSticks = new bool();
            string output = WordBuilder.ObscureWord(word);

            HangManBox.Image = Drawing.Drawer.DrawHangMan(hangman.HangmanStep);

            WordBox.Text = output;
            GuessedLetters.Text = ReturnGuessedLetters(incorrectGuesses);
            MessageBox.Show("");

            //After guessing check if we've guessed all letters in the word
            if (CompareLists(word.dontObscure, word.letters.Distinct().ToList()))
            {
                System.Threading.Thread.Sleep(5000);
                complete = true;
                break;
            }

            Thread readKeyThread = new Thread(InitReadKeyForm);
            readKeyThread.Start();
            readKeyThread.Join();

            if (ReadKeyForm.Key == 2) {
                w_reduceSticks = GuessWord();
            }
            else {
                w_reduceSticks = GuessLetters(ReadKeyForm.Key);
            }

            if (w_reduceSticks)
            {
                hangman.HangmanStep++;
            }
        } while (!complete);

【问题讨论】:

  • 由于Thread.Sleep(2000); 后面的break,您的代码的下部无法访问。但这是另一个问题。
  • 对,这只是我在调试一些东西,我要编辑它。

标签: c# multithreading winforms


【解决方案1】:

为什么你认为这是在一个单独的线程中运行的?从各种迹象来看,很明显它是在 UI 线程上运行的(您无法从 UI 线程之外修改 UI 元素)。这也是MessageBox.Show“帮助”的原因。 Application.DoEvents 也会这样做。

无论如何,问题的核心在于您实际上并没有在单独的线程中运行它。向我们展示您用于“启动新线程”的代码。

【讨论】:

  • 你是对的,它不是一个单独的线程,我是从与游戏表单相同的线程调用它。如果我将它分离到另一个线程,我将不得不修改我的代码,或者找到一种简单的方法来更新游戏回合线程中的 UI 元素。
  • @OverlyExcessive 这实际上很简单 - 您可以使用 Control.Invoke 将代码排队,以便在最近的方便时在 UI 线程上运行 :) 所以请完成所有工作和逻辑,以及何时更新用户界面,只需Invoke 代码。此外,如果您可以访问 .NET 4.5.1,您可能想要使用 await / async,这对于不受 CPU 限制的异步代码来说非常棒(在您的情况下,唯一的阻塞是等待一个键(I/O 绑定)和Thread.Sleep(一个坏主意)。
【解决方案2】:

您应该始终在创建它们的同一线程上更新您的 UI 控件。

看看这个http://www.codeproject.com/Articles/52752/Updating-Your-Form-from-Another-Thread-without-Cre,它解释了如何在正确的线程上更新控件

【讨论】:

    【解决方案3】:

    正如人们所建议的那样,您的 ui 线程没有正确更新的原因是因为在游戏线程中的代码执行后它没有获得这样做的优先级。 这可以通过添加Application.DoEvents()(这会告诉系统几乎让其他人也可以工作)或使用 Lock 对象来同步两个线程来解决。 作为旁注,因为我也开始阅读游戏编程,所以似乎实现游戏的正确方法(至少直到你进入严肃游戏的地步,我不知道那里发生了什么)是什么沿着这些思路:

    while (!finished){
        ProcessInputs();
        DoCalculations();
        DrawScreen();
    }
    

    因此,在这些应用程序中,似乎都是在同一个线程中完成的。而且它有点道理,因为它不像商业程序,您可以在其中发送 sql 查询,并且您不在乎它需要多长时间才能向用户显示结果。在游戏中,您必须对用户的行为进行即时反馈。
    希望我有所帮助

    【讨论】:

    • 大多数游戏仍然主要是单线程的原因是要制作一个出色的多线程游戏引擎是多么困难。 60 FPS 意味着您只有 16 毫秒的时间来处理每一帧,并且同步不同的线程,尤其是在那个时间范围内,是非常棘手的。在其他软件中工作的方法通常无法使用(不可变对象非常适合并行化,但在这种低延迟场景中您通常无法承受开销)。您的示例(SQL 查询)实际上并不相关,因为这是经典的 I/O 绑定操作 - 您可以在同一线程上使用 async 处理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-22
    • 2014-04-21
    • 1970-01-01
    • 2020-11-29
    • 2020-11-10
    • 2014-06-13
    相关资源
    最近更新 更多