LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

C# LongRunningTask-正确用法

freeflydom
2025年8月8日 9:4 本文热度 97

在上一篇文章《如何正确实现一个 BackgroundService》中有提到 LongRunning 来优化后台任务始终保持在同一个线程上。

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            return Task.Factory.StartNew(async () =>
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    // Simulate some work
                    Console.WriteLine("HostServiceTest_A is doing work.");
                    LongTermTask();
                    await Task.Delay(1000, stoppingToken); // Delay for 1 second
                }
                Console.WriteLine("HostServiceTest_A task done.");
            }, TaskCreationOptions.LongRunning);
        }
        private void LongTermTask()
        {
            // Simulate some work
            Console.WriteLine("LongTermTaskA is doing work.");
            Thread.Sleep(30000);
        }

但是被 大佬指出这个用法是错误的:以上用法并不能保证任务始终在同一个 Task(线程) 上执行。原因是当碰到第一个 await 之后运行时会从 ThreadPool 中调度一个新的线程来执行后面的代码,而当前线程被释放。这个时候就不符合我们使用 LongRunning 的期望了。

在 .NET 中,Task.Factory.StartNew 提供了 TaskCreationOptions.LongRunning 选项,很多开发者会用它来启动长时间运行的任务,并且想当然的认为它会永远执行在同一个线程上。但是事实上当遇到 async await 的时候并想象的那么简单。

下面我们还是通过一个错误的示例开始讲解如何正确的使用它。

错误用法

很多人会直接在 Task.Factory.StartNew 里传入一个 async 方法:

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
var task = Task.Factory.StartNew(async () =>
{
    Console.WriteLine($"long running task starting. Thread id: {Thread.CurrentThread.ManagedThreadId}");
    var loopCount = 1;
    while (true)
    {
        Console.WriteLine($"\r\nStart: loop count: {loopCount}, Thread id: {Thread.CurrentThread.ManagedThreadId}");
        await LongRunningJob();
        Console.WriteLine($"End: loop count: {loopCount}, Thread id: {Thread.CurrentThread.ManagedThreadId} \r\n ");
        loopCount++;
    }
}, TaskCreationOptions.LongRunning);
static async Task LongRunningJob()
{
    Console.WriteLine($"task doing. Thread id: {Thread.CurrentThread.ManagedThreadId}");
    await Task.Delay(1000);
}
Console.ReadLine();

输出:

Hello, World!
long running task starting. Thread id: 12
Start: loop count: 1, Thread id: 12
task doing. Thread id: 12
End: loop count: 1, Thread id: 11
Start: loop count: 2, Thread id: 11
task doing. Thread id: 11
End: loop count: 2, Thread id: 11

可以看到,第一次循环后,线程 id 发生了变化。很明显 LongRunning 失效了。原因开篇已经讲了,不在赘述。

正确用法 1:同步方法

LongRunningJob 改为同步方法,避免异步切换线程:

var task = Task.Factory.StartNew(() =>
{
    Console.WriteLine($"long running task starting. Thread id: {Thread.CurrentThread.ManagedThreadId}");
    var loopCount = 1;
    while (true)
    {
        Console.WriteLine($"\r\nStart: loop count: {loopCount}, Thread id: {Thread.CurrentThread.ManagedThreadId}");
        LongRunningJob();
        Console.WriteLine($"End: loop count: {loopCount}, Thread id: {Thread.CurrentThread.ManagedThreadId} \r\n ");
        loopCount++;
    }
}, TaskCreationOptions.LongRunning);
static void LongRunningJob()
{
    Console.WriteLine($"task doing. Thread id: {Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(1000);
}

输出:

Hello, World!
long running task starting. Thread id: 12
Start: loop count: 1, Thread id: 12
task doing. Thread id: 12
End: loop count: 1, Thread id: 12

线程 id 始终不变,说明始终运行在专用线程上。

正确用法 2:异步方法同步等待

如果必须用异步方法,可以用 .Wait() 让调用变为同步:

var task = Task.Factory.StartNew(() =>
{
    Console.WriteLine($"long running task starting. Thread id: {Thread.CurrentThread.ManagedThreadId}");
    var loopCount = 1;
    while (true)
    {
        Console.WriteLine($"\r\nStart: loop count: {loopCount}, Thread id: {Thread.CurrentThread.ManagedThreadId}");
        LongRunningJob().Wait();
        Console.WriteLine($"End: loop count: {loopCount}, Thread id: {Thread.CurrentThread.ManagedThreadId} \r\n ");
        loopCount++;
    }
}, TaskCreationOptions.LongRunning);
static async Task LongRunningJob()
{
    Console.WriteLine($"task doing. Thread id: {Thread.CurrentThread.ManagedThreadId}");
    await Task.Delay(1000);
}

输出:

Hello, World!
long running task starting. Thread id: 12
Start: loop count: 1, Thread id: 12
task doing. Thread id: 12
End: loop count: 1, Thread id: 12

总结

  • TaskCreationOptions.LongRunning 适用于同步、阻塞型任务。
  • 不要在 StartNew 里直接用 async 方法。
  • 如果必须用异步方法,需同步等待(如 .Wait())。

希望本文能帮你正确理解和使用 LongRunning 任务!

转自https://www.cnblogs.com/kklldog/p/19022317


该文章在 2025/8/8 9:04:45 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved