Execute certain background Tasks in separate ThreadPool to avoid starvation to critical Tasks executed in main thread(在单独的线程池中执行某些后台任务,以避免对主线程中执行的关键任务造成饥饿)
问题描述
在单独的线程池中执行某些后台任务(不是线程),以避免对主线程中执行的关键任务(不是线程)造成饥饿
我们的方案
我们托管了一个大容量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缓存问题等,这会导致这些后台线程延迟,这意味着由于数据量大,挂起任务总数将会增加。
这会造成这样一种情况,即API的较新执行不能调度高优先级任务,因为许多后台任务都在队列中。
我们尝试的解决方案
将TaskCreationOptions.LongRunning添加到高优先级任务将立即执行。 然而,这不是我们的解决方案,因为系统中到处都在调用很多任务,我们不能让它们在每个地方都长时间运行。 此外,WCF对传入API的处理将依赖于.NET线程池,而该线程池现在正处于匮乏状态。
通过信号量创建短路低主后台任务。仅在系统有能力处理线程时才派生线程(检查以前创建的线程是否已退出)。如果不是,那就不要产生线程。 例如,由于一个问题(比如数据库性能问题),大约有10,000个后台线程(非异步)处于IO等待状态,这可能会导致.NET主线程池中的线程不足。 在这个特定的例子中,我们可以添加一个信号量来将创建限制为100个,因此如果100个任务停滞,第101个任务一开始就不会被创建。
/li>
询问替代解决方案
有没有一种方法可以专门在"自定义线程/线程池"上生成"任务",而不是默认的.NET线程池。 这是我提到的后台任务,所以万一它们延迟了,它们不会拖累整个系统。 可以覆盖并创建要传递到Task.Factory.StartNew()中自定义TaskScheduler,因此,创建的任务不会位于默认的.NET线程池中,而是位于某些其他自定义池中。推荐答案
这里有一个静电RunLowPriority
方法,您可以用它来代替Task.Run
。它具有针对简单任务和泛型任务以及针对普通委托和异步委托的重载。
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
线程上运行,但所有可用ThreadPool
线程中最多可以有2个线程并发分配给RunLowPriority
任务。
SynchronizingObject
属性设置为null
的Elapsed
事件也在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
});
};
这篇关于在单独的线程池中执行某些后台任务,以避免对主线程中执行的关键任务造成饥饿的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:在单独的线程池中执行某些后台任务,以避免对主线程中执行的关键任务造成饥饿
- 良好实践:如何重用 .csproj 和 .sln 文件来为 CI 创建 2022-01-01
- 在哪里可以找到使用中的C#/XML文档注释的好例子? 2022-01-01
- C# 中多线程网络服务器的模式 2022-01-01
- 带有服务/守护程序应用程序的 Microsoft Graph CSharp SDK 和 OneDrive for Business - 配额方面返回 null 2022-01-01
- 如何用自己压缩一个 IEnumerable 2022-01-01
- 输入按键事件处理程序 2022-01-01
- Web Api 中的 Swagger .netcore 3.1,使用 swagger UI 设置日期时间格式 2022-01-01
- MoreLinq maxBy vs LINQ max + where 2022-01-01
- C#MongoDB使用Builders查找派生对象 2022-09-04
- WebMatrix WebSecurity PasswordSalt 2022-01-01