了解 .NET Core 应用程序中的线程饥饿
大多数应用程序在扩展时都将面临性能问题,这可能是由于线程不足造成的。在进入线程饥饿问题之前,我们需要了解线程池是如何工作的。
线程池是 Dotnet 的线程管理或线程排队机制。在硬件层面,我们将拥有一组 CPU 和 x2 处理器,例如,如果我们有 4 个内核的硬件,它将容纳 8 个逻辑处理器。每个处理器将在任何给定时间执行一个线程。对于本文,假设我们使用 8 个逻辑处理器。
系统是这样工作的:- 一旦应用程序启动,线程池将创建一组线程并使它们可用于处理传入的请求。它产生的线程数取决于主机系统上可用的处理器数量。在我们的例子中,它是 8 个线程。当新请求到来时,它最初会在任务队列上等待,然后运行时将根据此全局线程池中可用的线程选择此请求,如下图所示。
假设我们一次处理了大约 100 个请求,现在这些请求将驻留在任务队列中,并且我们的线程池最初能够处理 8 个线程。系统将从任务队列中挑选每个请求并在线程池中分配一个可用线程。现在,由于没有可用线程并且任务队列中等待的请求超过 92 个,运行时将产生新线程并使它们在线程池中可用。但是,我们的系统在任何给定时间只能处理 8 个线程,即使我们创建一个新线程也会处于等待处理状态。线程创建和上下文切换很昂贵,因为它使用大量内存。如果创建和销毁太多线程会消耗大量内存并导致系统停止并重新启动。
为了避免这个 Dotnet 线程池有一个节流机制。该机制将在达到最小阈值限制时激活,即达到最小 spawn 线程数。让我们通过一个简单的例子来理解这个机制。让我们继续上面的 100 个请求示例,系统能够在任何给定时间处理 8 个请求。通常,一旦任何请求完成其进程,分配的线程将被释放并使其在线程池中可用以服务任务队列上的另一个请求。这样,我们就可以避免在运行时创建新线程并使用昂贵的系统资源。每个开发人员都应该对他们的逻辑实现异步调用,以确保线程将返回线程池以供另一个请求重用,直到系统执行任何 I/O 处理。
根据节流机制,我们需要定义一个整数值作为阈值限制。例如,如果我们将此值设置为 50,并且运行时将继续创建新线程直到 50 个请求,从而从第 51 个请求运行时等待 0.5 秒,然后再创建新线程,在此期间,如果任何现有线程返回到该线程池将被重用并避免创建新线程。如果有 60 个请求,第 61 个请求在被任何线程分配之前应该等待 10*0.5 = 5 秒(假设所有线程都很忙并且在给定时间内没有可用线程),这称为线程饥饿。
我们可以在应用程序启动时使用以下语句将自定义值设置为节流阈值限制。
ThreadPool.SetMinThreads(50, 100);
第一个参数是阈值自定义值,第二个参数表示 IOCP 上的 I/O 线程计数。
如果我们将更高的值设置为节流阈值限制将导致新请求的等待时间更长,但它可以确保系统将继续响应而不会停止。我们需要有ConcurrencyLimiter这样的工具 来解决这些问题,对于超过阈值限制的所有新请求,它都会返回503错误,这样就不会有额外的新请求在任务队列中等待,直到任何线程在线程池上可用。
常见问题FAQ
- 程序仅供学习研究,请勿用于非法用途,不得违反国家法律,否则后果自负,一切法律责任与本站无关。
- 请仔细阅读以上条款再购买,拍下即代表同意条款并遵守约定,谢谢大家支持理解!