c# - 我可以创建一个可以修改用户界面并且可以中止的线程吗?

标签 c# .net user-interface multithreading

我的表单上有一个冗长的用户界面操作,每当触发事件时就会触发该操作。我希望在另一个线程中执行该操作,并在事件再次触发时中止该线程并重新启动,而不是在操作发生时让 UI 阻塞。

但是,为了安全地更改表单上的控件,我需要使用表单的 Invoke 或 BeginInvoke 方法。如果我这样做,那么我可以将所有 UI 操作放在一个函数中,如下所示:

private delegate void DoUIStuffDelegate(Thing1 arg1, Thing2 arg2);
private void doUIStuff(Thing1 arg1, Thing2 arg2)
{
    control1.Visible = false;
    this.Controls.Add(arg1.ToButton());
    ...
    control100.Text = arg2.ToString();
}

...

private void backgroundThread()
{
    Thing1 arg1 = new Thing1();
    Thing2 arg2 = new Thing2();

    this.Invoke(new DoUIStuffDelegate(doUIStuff), arg1, arg2);
}

Thread uiStuffThread = null;

public void OnEventFired()
{
    if (uiStuffThread != null)
        uiStuffThread.Abort();

    uiStuffThread = new Thread(backgroundThread);
    uiStuffThread.Start();
}

但如果我这样做,那么我就失去了在单独线程中工作的好处。或者,我可以将它们分别放入自己的函数中,如下所示:

private delegate void DoUIStuffLine1Delegate();
private delegate void DoUIStuffLine2Delegate(Thing1 arg1);
...

private delegate void DoUIStuffLine100Delegate(Thing2 arg2);

private void doUIStuffLine1()
{
    control1.Visible = false;
}

private void doUIStuffLine2()
{
    this.Controls.Add(arg1.ToButton());
}

...

private void doUIStuffLine100(Thing2 arg2)
{
    control100.Text = arg2.ToString();
}

...

private void backgroundThread()
{
    Thing1 arg1 = new Thing1();
    Thing2 arg2 = new Thing2();

    this.Invoke(new DoUIStuffLine1Delegate(doUIStuffLine1));
    this.Invoke(new DoUIStuffLine2Delegate(doUIStuffLine2), arg1);
    ...
    this.Invoke(new DoUIStuffLine100Delegate(doUIStuffLine100), arg2);
}

Thread uiStuffThread = null;

public void OnEventFired()
{
    if (uiStuffThread != null)
        uiStuffThread.Abort();

    uiStuffThread = new Thread(backgroundThread);
    uiStuffThread.Start();
}

但这是一个可怕的、无法维护的困惑。有没有办法创建一个可以修改用户界面并且可以中止的线程?这样我就可以做这样的事情:

private void doUIStuff()
{
    Thing1 arg1 = new Thing1();
    Thing2 arg2 = new Thing2();

    control1.Visible = false;
    this.Controls.Add(arg1.ToButton());
    ...
    control100.Text = arg2.ToString();
}

Thread uiStuffThread = null;

public void OnEventFired()
{
    if (uiStuffThread != null)
        uiStuffThread.Abort();

    uiStuffThread = this.GetNiceThread(doUIStuff);
    uiStuffThread.Start();
}

无需禁用表单上的跨线程检查?理想情况下,我希望能够在线程或方法上设置一些属性,这些属性将所有操作单独包装在委托(delegate)中,然后在表单的线程上调用。

最佳答案

首先 - 不要禁用跨线程检查...表单具有线程关联性...

第二 - 尽量避免中止线程;这不太好 - 你应该更喜欢干净关闭(例如BackgroundWorker支持的取消)

一种选择可能是编写一个包装方法:

  • 接受类型化委托(delegate)(因此您可以更简单地调用它)
  • 进行必要的检查(抛出异常以终止并展开)

例如:

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        try {
          Action<Action> update = thingToDo =>
          {
              if (worker.CancellationPending) throw new SomeException();
              this.Invoke(thingToDo);
          };

          //...
          string tmp = "abc"; // long running
          update(() => this.Text = tmp);

          tmp = "def"; // long running
          update(() => textbox1.Text = tmp);
        } catch (SomeException) {
          e.Cancel = true;
        }
    }

这仍然有点困惑,但可以说比左右中止线程更干净......

关于c# - 我可以创建一个可以修改用户界面并且可以中止的线程吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/302086/

相关文章:

.net - 如何使用 LINQ 将两个 Sum 操作合二为一?

c# - 减少字典中的所有 int 值

.net - 如何避免 ASP.NET 应用程序中的 SQL 注入(inject)攻击?

java - 我无法让我的程序等到 GUI 完成收集请求的信息(Java)

c# - 当 EventLogEntry.Index 达到 MaxInt 时会发生什么?

c# - 如何使用 SQLite.Net 设置多个 PRAGMA?

c# - 完全信任和中度信任问题

javascript - 单击按钮后更改 bool 值的状态

javascript - 跟踪复选框状态并为 ng-model 使用多个数据

java - 更改 JComboBox 的字符串数组