惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Spread Privacy
Spread Privacy
T
The Exploit Database - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 热门话题
T
Threat Research - Cisco Blogs
T
Tenable Blog
TaoSecurity Blog
TaoSecurity Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
AI
AI
P
Proofpoint News Feed
A
About on SuperTechFans
P
Privacy International News Feed
月光博客
月光博客
雷峰网
雷峰网
S
Secure Thoughts
博客园 - 叶小钗
博客园 - 聂微东
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Project Zero
Project Zero
The Cloudflare Blog
SecWiki News
SecWiki News
The Hacker News
The Hacker News
V
Vulnerabilities – Threatpost
罗磊的独立博客
A
Arctic Wolf
阮一峰的网络日志
阮一峰的网络日志
Know Your Adversary
Know Your Adversary
酷 壳 – CoolShell
酷 壳 – CoolShell
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Troy Hunt's Blog
The Last Watchdog
The Last Watchdog
Schneier on Security
Schneier on Security
小众软件
小众软件
有赞技术团队
有赞技术团队
博客园 - 司徒正美
T
Tailwind CSS Blog
量子位
C
Cybersecurity and Infrastructure Security Agency CISA
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Security @ Cisco Blogs
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
L
Lohrmann on Cybersecurity

博客园 - Agile.Zhou

AgileAI - 一个新的 .NET AI 库 并发,并行与异步 为什么说 IO 操作异步才有意义 自定义 Visual Studio 主题 使用 Azure AI Foundry 微调模型 SK + Neo4j 实现简单问答系统 AgileConfig-1.11.0 发布:增强的权限管理 如何正确实现一个 BackgroundService Dynamic adaptation to application sizes (DATAS) GC 策略 使用 AutoGen Studio 打造你的私有团队 在 Aspire 项目下使用 AgileConfig 使用 SK 进行向量操作 本地部署 DeepSeek Janus Pro 文生图大模型 Kernel Memory 让 SK 记住更多内容 .NET 依赖注入中的 Captive Dependency 在 Development 环境下依赖注入的行为可能有所不同 使用 SK Plugin 给 LLM 添加能力 使用 SemanticKernel 对接 Ollma 在 Github Action 管道内集成 Code Coverage Report
LongRunningTask-正确用法
Agile.Zhou · 2025-08-04 · via 博客园 - Agile.Zhou

在上一篇文章《如何正确实现一个 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/eventhorizon/p/17497359.html