gpt4 book ai didi

c# - 在单独的 ThreadPool 中执行某些后台任务,以避免在主线程中执行的关键任务被饿死

转载 作者:行者123 更新时间:2023-12-03 17:32:00 25 4
gpt4 key购买 nike

在单独的 ThreadPool 中执行某些后台任务(不是线程)以避免在主线程中执行的关键任务(不是线程)饥饿

我们的情景

我们托管了一个大容量的 WCF Web 服务,它在逻辑上具有以下代码:

void WcfApiMethod()
{
// logic

// invoke other tasks which are critical
var mainTask = Task.Factory.StartNew(() => { /* important task */ });
mainTask.Wait();

// invoke background task which is not critical
var backgroundTask = Task.Factory.StartNew(() => { /* some low-priority background action (not entirely async) */ });
// no need to wait, as this task is best effort. Fire and forget

// other logic
}

// other APIs

现在,问题是,在某些情况下,低优先级的后台任务可能需要更长的时间(约 30 秒),例如检测 SQL 连接问题、数据库性能问题、redis 缓存问题等,这会使那些后台线程延迟,这意味着 TOTAL PENDING TASK COUNT 将增加,因为量大。

这会产生一种情况,即 API 的较新执行无法调度高优先级任务,因为大量后台任务在队列中。

我们尝试过的解决方案
  • 将 TaskCreationOptions.LongRunning 添加到高优先级任务将立即执行它。
    然而,这对我们来说不是一个解决方案,因为系统中到处都有很多任务被调用,我们不能让它们在任何地方都长时间运行。
    此外,WCF 对传入 API 的处理将依赖 .NET 线程池,该线程池现在处于匮乏状态。
  • 通过 Semaphore 创建短路低优先级背景任务。仅在系统有能力处理线程时才生成线程(检查早期创建的线程是否已退出)。如果没有,就不要产生线程。
    例如,由于某个问题(比如 DB 性能问题),大约 10,000 个后台线程(非异步)处于 IO 等待状态,这可能会导致 .net 主线程池中的线程不足。
    在这种特定的情况下,我们可以添加一个信号量来限制创建 100 个,所以如果 100 个任务被卡住,第 101 个任务将不会被创建。

  • 询问替代解决方案

    有没有办法专门在“自定义线程/线程池”上生成“任务”,而不是默认的 .NET 线程池。
    这是针对我提到的后台任务,所以万一它们被延迟,它们不会随它们一起关闭整个系统。
    可能会被覆盖并创建一个自定义 TaskScheduler 以传递给 Task.Factory.StartNew() 因此,创建的任务不会在默认的 .NET 线程池上,而是在其他一些自定义池上。

    最佳答案

    这是一个静态 RunLowPriority可以用来代替 Task.Run 的方法.它具有简单和通用任务的重载,以及普通和异步委托(delegate)。

    const int LOW_PRIORITY_CONCURRENCY_LEVEL = 2;
    static TaskScheduler LowPriorityScheduler = new ConcurrentExclusiveSchedulerPair(
    TaskScheduler.Default, LOW_PRIORITY_CONCURRENCY_LEVEL).ConcurrentScheduler;

    public static Task RunLowPriority(Action action,
    CancellationToken cancellationToken = default)
    {
    return Task.Factory.StartNew(action, cancellationToken,
    TaskCreationOptions.DenyChildAttach, LowPriorityScheduler);
    }

    public static Task RunLowPriority(Func<Task> function,
    CancellationToken cancellationToken = default)
    {
    return Task.Factory.StartNew(function, cancellationToken,
    TaskCreationOptions.DenyChildAttach, LowPriorityScheduler).Unwrap();
    }

    public static Task<TResult> RunLowPriority<TResult>(Func<TResult> function,
    CancellationToken cancellationToken = default)
    {
    return Task.Factory.StartNew(function, cancellationToken,
    TaskCreationOptions.DenyChildAttach, LowPriorityScheduler);
    }

    public static Task<TResult> RunLowPriority<TResult>(Func<Task<TResult>> function,
    CancellationToken cancellationToken = default)
    {
    return Task.Factory.StartNew(function, cancellationToken,
    TaskCreationOptions.DenyChildAttach, LowPriorityScheduler).Unwrap();
    }
    通过 RunLowPriority 计划的操作方法将在 ThreadPool 上运行线程,但最多 2 个可用 ThreadPool线程可以同时分配给 RunLowPriority任务。
    请记住 Elapsed事件 System.Timers.Timer 有它的 SynchronizingObject属性设置为 nullThreadPool 中运行线程也。因此,如果您在此处理程序中执行低优先级工作,您可能应该通过相同的有限并发调度程序来调度它:
    var timer = new System.Timers.Timer();
    timer.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) =>
    {
    Thread.Sleep(10); // High priority code
    var fireAndForget = RunLowPriority(() =>
    {
    if (!timer.Enabled) return;
    Thread.Sleep(1000); // Simulate long running code that has low priority
    });
    };

    关于c# - 在单独的 ThreadPool 中执行某些后台任务,以避免在主线程中执行的关键任务被饿死,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60899507/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com