【问题标题】:Should I not use TPL when I need scalability?当我需要可扩展性时,我应该不使用 TPL 吗?
【发布时间】:2014-04-16 06:28:59
【问题描述】:

线程池线程是重要的可重用线程(例如在 asp.net 中)- 有助于处理请求。

相对于原语new Thread().start(....),它不使用线程池线程,并且不支持取消标记、连续性、结果值——毫无疑问,TPL 是首选策略。

但问题是Task 也是 uses 线程池线程。

在 TPL 中,TaskScheduler 负责实际对任务进行排队 准备执行。默认调度程序将使用线程池。

这让我想知道:

假设我有一个有许多并发用户的站点,我需要为每个用户执行 3 个计算绑定任务(非 IO)。

我担心线程池将没有线程剩余(因为我将通过使用线程池线程的 TASK 执行 3 个计算操作(针对每个用户)。 - 这将导致创建新的线程池线程。

这使我得出结论,当我需要可扩展性时 - 最好使用旧时尚 new Thread().start(...)

我错过了什么?我们回到原点了吗?

【问题讨论】:

  • 我会考虑将这些任务放到另一个进程中,这样你就不会饿死asp.net。新进程 = 新的线程池线程集,在该进程中,它们将专用于这些任务。因此,例如,Windows 服务和从网站到服务的某种形式的进程间通信。
  • 但是控制线程池比控制您创建的线程数要容易得多。如果您创建大量线程,您可能会获得更差的性能。
  • @Damien_The_Unbeliever 什么 ipc 通信?插座 ?嗯?我不想为此使用 wcf。最快的方法是什么?你能提供链接吗?

标签: c# multithreading .net-4.0 task-parallel-library


【解决方案1】:

您很少需要在 ASP.NET 中使用 Task.RunTask.Factory.StartNew。这些 API 用于 CPU 密集型工作,在处理 HTTP 请求时将 CPU 密集型工作卸载到另一个线程通常没有意义(与客户端 UI 应用程序不同)。那只会损害可扩展性。只需在当前线程上执行工作即可。

如果您需要跨多个 HTTP 请求生成具有生命周期的长期运行任务,请使用 cmets 中 @Damien_The_Unbeliever 建议的单独进程,例如,在同一主机或单独主机上的 WCF 服务。

也就是说,您仍然可以将一个非池线程包装为 Task 以及所有好东西,至少在当前的 TPL 实现中是这样。它是在您使用 TaskCreationOptions.LongRunningTask.Factory.StartNew 时创建的。在这样的线程中,Thread.IsThreadPoolThread 将是 false。这可以防止ThreadPool 饥饿,但肯定会增加进程的工作集。此外,此类线程不会像ThreadPool 线程那样被 TPL 重用,因此重复创建非池线程可能会非常昂贵。

作为TaskCreationOptions.LongRunning 的替代方案,使用TaskCompletionSource 可以轻松地将new Thread().Start() 包装为Task,具有相同的取消、异常和结果传播逻辑。

另一个想法是使用自定义任务调度程序,它总是将任务排入新线程:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(new { Thread.CurrentThread.ManagedThreadId });

        var task = Task.Factory.StartNew(
            () => Console.WriteLine(new { 
                Thread.CurrentThread.IsThreadPoolThread,
                Thread.CurrentThread.ManagedThreadId}),
            CancellationToken.None,
            // hide the scheduler from inner tasks
            TaskCreationOptions.HideScheduler,
            NewThreadTaskScheduler.Scheduler);

        task.Wait();
    }
}

class NewThreadTaskScheduler : TaskScheduler
{
    public static readonly NewThreadTaskScheduler Scheduler = 
        new NewThreadTaskScheduler();

    NewThreadTaskScheduler()
    {
    }

    protected override void QueueTask(Task task)
    {
        var thread = new Thread(() =>
        {
            base.TryExecuteTask(task);
        });
        thread.IsBackground = true;
        thread.Start();
    }

    protected override bool TryExecuteTaskInline(
        Task task, 
        bool taskWasPreviouslyQueued)
    {
        return false;
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        return null;
    }

    public override int MaximumConcurrencyLevel { 
        get { return Int32.MaxValue; } }
}

【讨论】:

  • @RoyiNamir,另一个想法是使用简单的自定义任务调度程序,查看我发布的更新。
  • 你说不使用线程但在同一个线程上做是什么意思?我想使用我所有的 cors ....再次每个用户运行 3 个 cpu 任务,每个 5 秒(例如),所以我想使用 allcors,这就是为什么我理解你的句子:“就在当前执行线程“....你能详细说明一下吗?
  • @RoyiNamir,通常,在 ASP.NET 中,您希望为尽可能多的并发 HTTP 请求提供服务,这就是您需要 CPU 内核的目的。因此,您想尽可能地使用async 控制器方法,而您确实想使用Task.Run。除非您想将任何特定请求优先于其他请求,否则在您为该请求执行并行工作时它们将被搁置(以尽快将响应传递给客户端)。
猜你喜欢
  • 1970-01-01
  • 2011-11-07
  • 1970-01-01
  • 2012-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-18
  • 2016-06-30
相关资源
最近更新 更多