【问题标题】:How to create a thread with await methods inside?如何创建一个内部带有等待方法的线程?
【发布时间】:2026-01-11 20:15:02
【问题描述】:

我正在创建一个线程

Thread MyThread = new Thread(async () => await MyTask());
MyThread.Start(); 

这有点没有意义,这是如何正确创建它的问题。

MyTask 是一个无休止的任务,它会定期启动 CPU 密集型工作

async Task MyTask ()
    {
        while (true)
        {                
            await Task.Run(() => CPU_Load());
            await Task.Delay(1000).ConfigureAwait(false);
        }
    }

CPU_Load 是一种正在完成工作的方法

void CPU_Load ()
    {            
       *CPU_Load*    
    }


      

【问题讨论】:

  • 为什么不改用线程安全的System.Threading.Timer
  • 你的问题到底是什么?
  • 问题热点创建线程?正如我被告知线程类不知道异步/等待,所以它执行该方法直到它遇到第一个等待然后停止。
  • 我从未使用过计时器,我快速浏览了一下,我认为它不适合我。
  • @IllSkillz 为什么计时器不适合? “定期启动 cpu 密集型工作” - 这就是计时器的作用。如果您创建了一个运行代码、等待然后再次运行该代码的线程,那么您只是编写了自己的计时器。

标签: c# asynchronous


【解决方案1】:

Thread 构造函数不理解 async 委托。你可以在这里阅读:

定期执行某些代码的“正确”方式取决于您要在何处运行代码。你有什么理由在专用线程上运行它吗?一些组件是线程仿射的,并且在它们存在的整个过程中都需要由同一线程操作。如果你有这个(不是很常见的)需求,你可以使用 Thread 没有 async/await 的构造函数:

var myThread = new Thread(() =>
{
    while (true)
    {
        var delayTask = Task.Delay(1000);
        CPU_Load();
        delayTask.Wait();
    }
});
myThread.IsBackground = true;
myThread.Start();

注意Task.Delay 任务是如何在 CPU 密集型操作之前创建的,然后在之后等待。这样,CPU_Load 方法的两次后续调用之间的间隔将保持不变。它不取决于通话本身的持续时间。

如果您不需要专用线程,则可以通过使用来自ThreadPool 的可重用线程来更经济地完成工作。这正是您当前的 MyTask 实现所做的,其中包含 Task.Run 方法。启动任务就像调用异步方法一样简单:

var myTask = MyTask();

现在任务正在运行,并将继续运行直到进程终止,或者CPU_Load 调用失败,无论先发生什么。

实现异步MyTask 方法的另一种方法是将整个循环包装在Task.Run 中,而不是将Task.Run 放在循环中。在功能和性能方面几乎相同:

var myTask = Task.Run(async () =>
{
    while (true)
    {
        var delayTask = Task.Delay(1000);
        CPU_Load();
        await delayTask;
    }
});

我省略了ConfigureAwait(false),因为在这种情况下并不需要它。 ThreadPool 没有可以被await 捕获的同步上下文。

您也可以考虑使用Timer 定期运行您的工作,但我认为基于Task 的方法更胜一筹。使用Timer 执行具有恒定间隔的非重叠执行策略非常棘手。注意事项:

  1. System.Timers.Timeris not thread-safe
  2. 它允许事件处理程序的重叠调用。
  3. swallows 处理程序内部抛出的任何异常。
  4. Elapsed 事件将在 使用 Stop 方法停止 Timer 之后引发。
  5. 没有简单的方法可以停止计时器,然后等待所有正在运行的事件处理程序完成。
  6. 课程是一次性的,needs to be disposed

【讨论】:

    【解决方案2】:

    Flydog57 引用 a comment

    Main 不能异步时,原来的异步策略怎么样。使用非异步函数(或委托或 lambda)启动线程。在那个简单地说 WhatIWantToDoAsync().Wait(); 的函数中,它是异步的并返回一个任务。你的线程函数永远不会返回,所以等待它不会做太多。

    【讨论】:

    • 如果 flydog57 的建议有帮助,请让他/她留下一个帖子,以便他/她获得声誉。
    • 这可能是一种解决方法,但 Task.Wait 会阻塞调用线程,在这种情况下会阻塞应用程序的整个运行。所以新创建的线程只会运行到第一次挂起,然后阻塞直到while循环结束。你最好同步做所有事情并使用 Thread.Sleep。或者按照建议使用 Timer。