【问题标题】:Should I use threadpool,threads or backgroundworker with System.Timers.Timer?我应该在 System.Timers.Timer 中使用线程池、线程还是后台工作程序?
【发布时间】:2012-06-24 13:00:41
【问题描述】:
public class JobRunner
{
    internal Timer Timer;
    internal readonly IEnumerable<YuartzJob> Jobs;

    public void Start()
    {
        this.Timer = new Timer(5000);
        this.Timer.Elapsed += Timer_Elapsed;
        this.Timer.Start();
    }

    public void Stop()
    {
        this.Timer.Stop();
    }

    internal void Timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        foreach (var job in Jobs)
        {
            MyActivator.RunJob(job);
        }
    }
}

public class MyActivator
{
    public static void RunJob(YuartzJob job)
    {
        var objectHandle = Activator.CreateInstance(job.AssemblyName, job.ClassName).Unwrap();
        var currentJob = (IJob)objectHandle;
        currentJob.Run();
    }
}

我在使用 MyActivator.RunJob(job) 的地方应该怎么做:

  1. 我应该使用 ThreadPool 调用 MyActivator.RunJob 吗?
  2. 我应该使用线程调用 MyActivator.RunJob 吗?
  3. 我应该使用 BackgroundWorker 调用 MyActivator.RunJob 吗?
  4. 不做任何事情就可以这样工作吗?

【问题讨论】:

  • 您想并行运行这些作业吗?当前的解决方案在单独的线程上同步执行它们
  • @Panagiotis Kanavos :是的,我想并行运行这些作业。
  • 如果您的作业耗时超过 5 秒,计时器仍会触发,作业将无限排队。

标签: .net multithreading backgroundworker threadpool task-parallel-library


【解决方案1】:

.NET Framework 提供了四个计时器。其中两个是通用多线程计时器:

  • System.Threading.Timer
  • System.Timers.Timer

另外两个是专用的单线程定时器:

  • System.Windows.Forms.Timer(Windows 窗体计时器)
  • System.Windows.Threading.DispatcherTimer(WPF 计时器)

因此,如果您希望以多线程方式/并行方式执行您的方法,那么您必须选择前两个中的一个。

通过示例源代码here了解更多信息

【讨论】:

  • OP 已经使用 System.Timers.Timer。他问的是如何并行解雇所有工作
【解决方案2】:

如果您的目标是 .NET 4,最简单的方法是使用任务。任务使用 ThreadPool 中的线程执行方法。 .NET 运行时(实际上是一个 TaskScheduler)确保线程池不会被太多任务耗尽:

    foreach (var job in Jobs)
    {
        Task.Factory.StartNew(()=>MyActivator.RunJob(job));
    }

任务还允许轻松取消、异常处理和链接,这在使用 ThreadPool 或您自己的线程时是不可能的。

对于早期版本,您应该首先考虑 ThreadPool,并且仅在作业太多且运行时间长以至于有耗尽线程池的风险时才创建自​​己的线程:

    foreach (var job in Jobs)
    {
        ThreadPool.QueueUserWorkItem(o=>MyActivator.RunJob(job));
    }

Backgroundworker 在 .NET 4+ 中被认为是过时的,并且仅适用于非常粗粒度的并行性。在您的情况下,您必须为每个作业创建一个单独的工作人员,订阅其事件,运行它并在它之后进行清理。

【讨论】:

  • BackgroundWorker 并没有正式过时,我想说你也不应该认为它已经过时。它仍然在 GUI 应用程序中使用,特别是如果您想将后台任务的进度报告给 UI 线程。
  • 我最近一直在阅读 Albahari 的 C#5,猜它影响了我。无论如何,.NET 4.5 具有用于这项工作的 IProgress 接口和相关类。如果您使用 .NET 4.5 或 Async CTP,则不再需要 BW。
【解决方案3】:

如果您想高效地并行处理一组项目,我的首选将是专门为此目的而制作的:Parallel.ForEach()

在内部,它使用Tasks,这意味着它将在ThreadPool 上运行。它还尝试有效地使用Tasks,所以如果你有一个包含一百万个项目的集合,它不会创建一百万个Tasks。

【讨论】:

  • Parallel.ForEach 也将阻塞,直到所有作业完成。这可能是可取的,也可能不是可取的,这取决于场景或工作本身。顺便说一句,使用 Quartz.NET 这样的全功能调度程序而不是手动编码可能是一个更好的选择
  • @PanagiotisKanavos 是的,它会阻塞,但我认为在这种情况下没关系,因为它是从ThreadPool 线程开始的(因为Timer)。而且我认为在这里使用 Quartz.NET 之类的东西可能有点矫枉过正,但我​​从未使用过它。
猜你喜欢
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 2013-01-18
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多