【问题标题】:NullReferenceException @ Dispatcher & Other GUI IssuesNullReferenceException @ Dispatcher 和其他 GUI 问题
【发布时间】:2023-03-27 08:34:01
【问题描述】:

我一直在尝试实现多类线程 GUI 管理。正如在我希望不同的线程分布在不同的 .cs 文件中的多个类中,以根据需要更新 UI。

我搜索了 stackoverflow 和其他来源,发现大多数人使用 Dispatcher.Invoke 或类似的东西。所以我决定开始测试......

所以下面是一个名为 wThread.cs 的类中的一个线程,

public class wThread
{
    public EventHandler SignalLabelUpdate;
    public Dispatcher uiUpdate;

    public wThread()
    {
        uiUpdate = Program.myForm.dispat;
        //the uiUpdate seems to be null for some reason... If i am doing it wrong how do i get the dispatcher?
        Thread myThread = new Thread(run);
        myThread.Start();
    }
    Action myDelegate = new Action(updateLabel);
    // is there a way i can pass a string into the above so updatelabel will work? 

    public void updateLabel(String text)
    {
        if (SignalLabelUpdate != null)
            SignalLabelUpdate(this, new TextChangedEvent(text));
    }

    public void run()
    {
        while (uiUpdate == null)
            Thread.Sleep(500);
        for (int i = 0; i < 1000; i++)
        {
            //I hope that the line below would work 
            uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
            // was also hoping i can do the below commented code
            // uiUpdate.Invoke(myDelegate)
            Thread.Sleep(1000);
        }
    }
}

下面是我的 form1.cs,它是 Visual Studio 2012 的预加载代码,

public partial class Form1 : Form
{
    public Dispatcher dispat;
    public Form1()
    {
        dispat = Dispatcher.CurrentDispatcher;
        InitializeComponent();
        wThread worker = new wThread();
    }
}

我的大部分问题都在上面的 cmets 中,但这里列出了它们:

  1. 由于某种原因,uiUpdate 似乎为空...如果我做错了,我该如何获取调度程序? (wThread.cs 问题)

    uiUpdate = Program.myForm.dispat'
    
  2. 有没有办法我可以将字符串传递到上面,以便 updatelabel 可以工作?

    Action myDelegate = new Action(updateLabel);
    
  3. 我希望下面的行能正常工作

    uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
    
  4. 也希望我能做下面的注释代码

    uiUpdate.Invoke(myDelegate)
    

编辑:我将 wThread 构造函数 wThread worker = new wThread() 移出 form1 初始化区域......它修复了我的空指针。相反,我将 wThread 构造函数移动到构造表单的静态 main void 中......比如 Application.Run(myForm);

不幸的是,在我关闭 UI 之前 wThread 不会启动。对此最好的办法是什么?在 Application.Run 启动我的表单之前创建另一个线程并使用该线程启动我的真实线程?

【问题讨论】:

  • 您使用的是什么 UI 技术?我看到 Dispatcher 和 Form? WinForms 还是 WPF?
  • 实现后台线程工作的最简单方法是使用 MVVM(假设您在 WPF 中):stackoverflow.com/a/2034333/1561465

标签: c# multithreading user-interface


【解决方案1】:

@1:在Form1 构造函数中,在dispat = Dispatcher.CurrentDispatcher; 处放置一个断点并检查值是什么。可能只是 Dispatcher.CurrentDispatcher 为空,您必须稍后获得它,而不是在 ctor 中

@2 是的。使用Func&lt;string&gt; 而不是行动。 InvokeBeginInvoke 接受一个委托,还有一组params object[] 构成委托调用的参数。当然,传递的参数数组必须与委托签名完全匹配,因此在使用Func&lt;string&gt; 时,请使用只有一项的对象[]:字符串。

@3 - 是的,只要i 是一个可以被委托闭包捕获的简单局部变量,它就可以工作。如果它是 foreach 迭代器或一些非本地成员,它可能会出现问题,但是,不同的故事。

@4 - 是的,它会起作用,只要记住也要传递 Func&lt;string&gt; 的参数。

【讨论】:

  • 这更像是一个问题而不是一个答案,但是像stackoverflow.com/questions/22686467/… 中那样使用任务而不是 BeginInvoke 不是更好
  • @tradetree - 几乎任何东西都会更好,Rx,Task,甚至是好的旧 BackgroundWorker 就足够了,并且可以节省一些打字。但是,我的印象是 OP 专门询问了那套工具。最近,很容易找到关于Tasks的文章/教程,甚至关于通过BGW隐式使用线程的文章仍然存在,并且在询问winforms和threads时它们会从google弹出。所以,这一次,我只是想回答具体问题。
  • 嘿,还必须注意,在某些地方,任务及其整个底层框架实际上比“将此代码反弹到该调度程序/线程”+一些锁定或线程要复杂得多,也更难以完全理解同步 :) 但是,这不是地点/时间。我的意见:BackgroundWorker 适合初学者,Tasks 适合高级,BeginInvoke 适合绝望者,也就是“真的没有时间”/“做完就忘记”/“我写了就离开,再见”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-10
  • 2013-11-25
  • 2020-01-08
  • 2013-03-29
  • 1970-01-01
相关资源
最近更新 更多