c# - 当我需要可扩展性时,我不应该使用 TPL 吗?

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

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

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

但问题是 Task 还有 uses线程池线程。

In TPL, a TaskScheduler is responsible for actually queuing the tasks up for execution. The Default scheduler will use the thread-pool .



这让我想知道:

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

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

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

我错过了什么?我们回到第一方了吗?

最佳答案

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

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

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

作为 TaskCreationOptions.LongRunning 的替代品,很容易包装new Thread().Start()作为 Task使用 TaskCompletionSource ,具有相同的取消、异常和结果传播逻辑。

还有一个想法是使用自定义任务调度程序 它总是在一个新线程上排队任务:

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; } }
}

关于c# - 当我需要可扩展性时,我不应该使用 TPL 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23101686/

相关文章:

c# - 如何在使用 C# 和 SQL 的高事务应用程序中每 5 分钟记录一次对数据库的更改?

c# - 使用对象初始值设定项创建实例的表达式

multithreading - 什么是线程局部性

c# - 如何跨多个项目共享cshtml页面?

c# - 使用 Validator 时忽略 .NET 4 RTM MetadataType 属性

c# - 在 C# 中打开具有完全访问权限的 Excel 工作表

c# - 将 0.0 转换为 double 有什么问题?

java - 动态 JLabel 文本

python - 如何使用线程名从/proc中识别线程?

c# - 如何在 ConfigurationElementCollection 中拥有自定义属性?