【问题标题】:Asynchronous Delegates Vs Thread/ThreadPool?异步委托与线程/线程池?
【发布时间】:2010-12-22 17:13:34
【问题描述】:

我需要执行 3 个并行任务,完成每个任务后,他们应该调用相同的函数来打印结果。

我不明白 .net 中为什么我们有异步调用 (delegate.BeginInvoke() & delegate.EndInvoke()) 以及 Thread 类?

我有点困惑什么时候使用哪个?现在在这种特殊情况下,我应该使用什么异步调用或线程类?

我正在使用 C#。

【问题讨论】:

    标签: c# .net multithreading


    【解决方案1】:

    1.异步委托

    当您使用异步调用时 有应该处理的工作项目 在后台,你关心什么时候 他们完成了

    BackgroundWorker vs background Thread

    2。后台工作人员

    • 使用 BackgroundWorker 如果您有一个任务在 背景和需要与之交互 UI。 并在您不在乎他们何时完成他们的任务时使用它。整理数据的任务 和对 UI 线程的方法调用是 通过其自动处理 基于事件的模型。
    • 避免使用 BackgroundWorker 如果 (1) 您的程序集还没有 引用 System.Windows.Form 装配,(2)你需要线程 成为前台线程,或者 (3) 你 需要操纵线程 优先级。

    3。线程池

    • 在需要效率时使用 ThreadPool 线程。线程池 有助于避免相关的开销 创建、启动和停止 线程。
    • 避免使用 ThreadPool 如果 (1) 任务在您的生命周期内运行 应用程序,(2)你需要线程 成为前台线程,(3)你 需要操纵线程 优先级,或者 (4) 你需要线程 有一个固定的身份(中止, 暂停,发现)。

    4.线程类

    • 将 Thread 类用于长时间运行的任务以及当您 需要正式提供的功能 线程模型,例如,选择 在前景和背景之间 线程,调整线程优先级, 对线程的细粒度控制 执行等。

    【讨论】:

    • 使用线程类 (#4) 而不是任何基于线程池的替代方案 (#1-#3) 的另一种情况:如果您使用在线程静态字段中存储对象引用的任何类,除非或直到引用被删除或包含它们的线程退出,否则这些引用的直接或间接目标将不符合垃圾回收条件。如果线程静态引用存储在线程池线程中,它们可能会无限期地保留在那里,可能会造成大量内存泄漏。
    • 这似乎是最好的答案,但是有点担心没有人提到线程池饱和或建议运行代理的时间会影响您的决定。 ThreadPool/TaskPool 用于在后台线程上快速完成工作,但应限制在少于 50-150 毫秒的工作。投入繁重的工作(即超过 150 毫秒)会导致线程池饱和,然后当 ThreadPool 调整大小时,您可能会得到令人讨厌的 500 毫秒延迟。
    【解决方案2】:

    我不明白 .net 中为什么我们有异步调用 (delegate.BeginInvoke() & delegate.EndInvoke()) 以及 Thread 类?

    • 当您有应该在后台处理的工作项并且您关心它们何时完成时,使用异步调用。

    • 线程池适用于当您有应该在后台处理的工作项并且您不在乎它们何时完成时。

    • 线程用于做永远不会完成的事情。

    例子:

    如果您正在从磁盘读取大文件并且不想阻塞 GUI 线程,请使用异步调用。

    如果您在后台懒惰地写入一个或多个文件,请使用线程池。

    如果您每隔几秒就轮询文件系统以查找更改的内容,请使用线程。

    【讨论】:

    • 详细来说,.NET 中的大多数示例都有内置类,无需手动使用线程池即可处理这些问题。
    • 我看不出调用 QueueUserWorkItem 比调用 BeginInvoke 更“手动”。
    【解决方案3】:

    异步方法本质上抽象了工作实际处理的方式。它可能会衍生到一个新的进程中,它可能会在一个单独的线程中执行……没关系。

    重要的是你在说什么:

    1. 启动时运行此代码。
    2. 完成后运行此代码。

    如果可以选择,我将使用 API 异步方法,而不是每次都实现自己的线程机制。框架开发人员为您做了艰苦的工作,为什么要重新发明*。

    【讨论】:

      【解决方案4】:

      我不明白 .net 为什么我们有 异步调用 (delegate.BeginInvoke() & delegate.EndInvoke()) 以及 线程类?

      一般来说,即使在一个设计良好的系统中,总是会有多种方式来做一件事,因为有些事情是由低级设施构建而成的高级设施。如果有适合您需求的高级设施,那是您的幸运日。如果没有,您必须使用底层设施自己构建它。

      Thread 类使用 Win32 的 BeginThread,因此您不必这样做。线程池使用Thread 类,因此您不必这样做。 BeginInvoke 使用线程池,所以你不必,等等。

      【讨论】:

        【解决方案5】:

        使用线程池中的线程执行异步委托。这减少了手动创建线程和处理它的开销。 Threadpool 线程的开销比您手动创建的线程要少,并且必须被释放。

        此外,在手动创建的线程中执行方法可以让您获得更多控制权,例如中断线程、中止线程、检查其状态、设置其优先级等。

        如果您想快速使方法异步执行,则使用异步委托。

        此外,EndInvoke 允许您返回一个对象,以便您检索执行结果。 Thread.Join 虽然在功能上等价,但不允许您返回任何内容。

        【讨论】:

          【解决方案6】:

          这是一个早已被遗忘的话题,但这里根本没有提到的是有两种类型的工作,计算和 I/O 绑定。

          在计算绑定工作的情况下,这是在单独的线程中执行的(如果您使用 BackgroundWorkerBegin/End 模式,这来自线程池;或者,如果您决定创建自己的自定义线程线程)。

          另一方面,I/O 绑定在 I/O 端口中执行(硬件支持),不需要线程;文件访问和网络套接字是 I/O 绑定任务的两个示例。

          【讨论】:

            【解决方案7】:

            如果您有一个长时间运行的进程(例如表单应用程序、IIS、Web 服务、Windows 服务),您可能最好使用异步方法。线程需要系统资源和开销。我总是尽量避免创建线程。如果不能,我会尝试依靠 ThreadPool 来处理/管理它们。

            如果您能告诉我们您的应用程序是什么,您可能会获得更多适用的信息。

            BeginInvoke 在后台使用委托,而 Async 方法通常不这样做。

            【讨论】:

              【解决方案8】:

              Delegate.BeginInvoke 抓取一个线程池线程并在该线程上执行传递的委托。 因此,当您使用 BeginInvoke 时,.NET Framework 会执行很多工作,例如创建新线程、启动它等。

              你也可以看看 ThreadPool.QueueUserWorkItem(delegate { /* do stuff */ });寻找异步调用的替代方法。

              【讨论】: