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

在单独的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连接问题,DB性能问题,Redis缓存问题等,这将使这些后台线程延迟,这表示由于交易量大,待处理的总任务数将增加。

这会创建一个方案,在该方案中,较新的API执行无法安排高优先级任务,因为许多后台任务都在排队。

我们尝试的解决方案

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

询问替代解决方案

有没有一种方法可以专门在“自定义线程/线程池”而不是默认的.NET线程池上生成“任务”。
这是我提到的后台任务,因此,如果它们延迟了,它们不会使整个系统崩溃。
可能会被覆盖并创建一个自定义TaskScheduler传递给Task.Factory.StartNew(),因此,创建的任务将不在默认的.NET线程池上,而是在其他一些自定义池上。

参考方案

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

const int LOW_PRIORITY_CONCURRENCY_LEVEL = 100;
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();
}

请记住,其Elapsed属性设置为System.Timers.TimerSynchronizingObjectnull事件也将在ThreadPool线程中运行。因此,如果您要在此处理程序中进行低优先级的工作,则可能应该通过相同的受限并发调度程序进行调度:

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

剃刀付款集成->如何通过关闭按钮X检测剃刀付款模型是否关闭 - javascript

当用户关闭而无需付款时,我在CI框架中使用Razorpay,请创建razor支付模型,然后取消订单,我希望按状态更改为已取消的状态触发查询。所以我怎么能检测到这一点。我已经通过单击jQuery单击关闭功能但无法使用... javascript大神给出的解决方案 Razorpay提供了JS方法来检测模式关闭。您编写的任何JS代码都不会在结帐页面上运行,因为它是…

如何使用箭头符号(->)创建受保护的方法? - java

当我们编写以下代码时Stream.of(1,2,3,4,5).filter(i -> (i%2 == 0)).map( i -> i*i ); 表达式i -> (i%2 == 0)或i -> i*i将变为私有方法。在我的用例中,编写了一个junit测试,以确保没有方法是私有的(是的,这是强制性的),并且对于这些lambda表达式而言,…

粗糙的Unicode->没有CLDR的语言代码? - javascript

我在写字典应用。如果用户键入Unicode字符,我想检查该字符是哪种语言。例如字 - returns ['zh', 'ja', 'ko'] العربية - returns ['ar'] a - returns ['en', 'fr', …

C#中的任务:错误传递双精度类型函数作为参数 - c#

我正在尝试执行预期收益为double类型的任务。作为参数,我还将任务传递给double类型的函数。下面是代码:using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sys…

将谓词<T>转换为Func <T,bool> - c#

我有一个包含成员Predicate的类,希望在Linq表达式中使用该类:using System.Linq; class MyClass { public bool DoAllHaveSomeProperty() { return m_instrumentList.All(m_filterExpression); } private IEnumerable&…