【问题标题】:Is it a good idea to move all non-UI code to a different thread?将所有非 UI 代码移动到不同的线程是个好主意吗?
【发布时间】:2018-08-23 04:18:03
【问题描述】:

我见过很多例子,其中长时间运行的代码是从事件处理程序异步执行的。例如,这是 WPF 中按钮单击的事件处理程序:

public async void Button_Click(object sender, EventArgs e)
{
    await Task.Run(() => DoWork());
}

private int DoWork()
{
    for (int i = 0; i < 10000000; i++) { }
    return 42;
}

如果DoWork 更复杂,比如写入数据库,那么多次单击按钮可能会导致多个线程同时尝试写入数据库。

如果不是有许多这样的Task.Run 调用,而是为所有非 UI 工作创建一个新的 Thread 会怎么样。任务可以使用BlockingCollection 之类的东西在这个线程上排队。

因为这个线程与 UI 线程是分开的,所以 UI 仍然是响应式的,并且多次单击按钮会将任务(按顺序)调度到同一个线程,从而避免并发问题。

这是个好主意吗?

【问题讨论】:

  • 如果您想避免多次单击,请禁用该按钮,直到任务完成。我会避免滚动我自己的任务系统。
  • 如果它是 IO 绑定的工作,比如与数据库交谈,它根本不应该使用 Task.Run,只是 async/await 一直调用。如果它是 CPU 密集型工作或 IO 不公开异步方法,您只需要启动一个线程。
  • 我支持@RonBeyer:为什么要允许用户进行多次点击而不等待上一次点击“结果”?它是应用程序及其用户的价值吗?大多数时候,事实并非如此。但这实际上取决于您的情况
  • 为什么要这样做?您希望从您提议的所有工作中获得什么?
  • @Servy 我希望在处理 UI 事件时获得一些一致性。例如,可能有一个类是所有要完成的非 UI 代码的“入口点”(并在其自己的线程上执行)。这将使新开发人员更难在 UI 线程上意外运行非 UI 代码。我也希望得到这样的想法,即无论正在完成的工作如何,UI都会做出响应——它可能很长,也可能很短——关键是我什至不必担心关于它:这一切都在自己的线程上完成。

标签: c# wpf multithreading asynchronous async-await


【解决方案1】:

您所说的是QueuedTaskScheduler,可以在并行扩展中找到。

话虽如此,我不会简单地将所有操作排队。虽然您现在拥有一个响应式 UI,但它看起来好像什么都没有发生。

我会毫不犹豫地做一个 Task.Factory.StartNew。不保证任务会启动新线程。任务和线程是两个不同的东西。默认任务调度程序足够聪明,可以确定是否有可用的资源来为该任务启动一个新线程。如果您同时请求一堆任务开始,排队将自动发生。

【讨论】:

    【解决方案2】:

    与许多事情一样,答案是视情况而定async/await 一直向下是 cmets 中提到的 I/O 绑定工作的一个很好的解决方案,并且是现代的首选方法。 Task.Run()也可以。

    你描述的模式可以当然可以用来做你想做的事,并且是保持 UI 响应的一个不错的解决方案。在将命令排队到线程之前,请禁用您的按钮。当您的命令完成时,您可以将按钮状态设置为启用(通过“启用/禁用按钮”,我的意思是更改视图模型上的 bool 值)。您的模式绝对适合 CPU 密集型工作,但 Task.Run()

    您的长时间运行的线程基本上是一个命令队列,如果您需要序列化工作,这可能是有利的。所以这可能是一个“好”的主意。

    【讨论】:

      【解决方案3】:

      对于长时间运行的操作,在大多数情况下使用异步是有意义的:Keep the UI thread responsive

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-05
        • 2023-04-06
        • 1970-01-01
        • 2021-10-27
        • 2013-04-05
        相关资源
        最近更新 更多