线程池 ThreadPool

news2024/12/23 13:32:05

一般情况下我们都使用Thread类创建线程,因为通过Thread对象可以对线程进行灵活 的控制。但过多创建线程和销毁线程,会消耗掉大量的内存和CPU资源, 假如某段时间内突然爆发了100个短小的线程,创建和销毁这些线程就会消耗很多时间, 可能比线程本身运行的时间还长。为了改善这种状况,.NET提供了一种称之为线程池 (Thread Pool)的技术。线程池提供若干个固定线程轮流为大量的任务服务,比如用10个 线程轮流执行100个任务,当一个线程完成任务时,并不马上销毁,而是接手另一个任务, 从而减少创建和销毁线程的消耗。 线程池由 System.Threading 命名空间中的 ThreadPool 类实现,其部分方法如表 所示。

 ThreadPool 是一个静态类,不必创建实例就可以使用它。一个应用程序最多只有一个 线程池,它会在首次向线程池中排入工作函数时自动创建。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace XianChengChi
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ThreadPoolTest();
           
            Console.ReadKey();  //按下任意键结束程序 
           
                      
        }
        public static void ThreadPoolTest()
        {
            //向线程池中添加100个工作线程 
            for (int i = 1; i <= 100; i++)
            {//ThreadPool.QueueUserWorkItem 是调用线程池来排队工作项的方法//WaitCallback 是一个委托类型,它指向一个没有返回值并接受一个 object 类型参数的方法。
                ThreadPool.QueueUserWorkItem(new WaitCallback(WorkFunction), i);//当 WorkFunction 被调用时,参数i会被作为 object 类型的参数传入
            }//创建了一个新的 WaitCallback 委托实例,它指向 WorkFunction 方法
        }
        //工作函数 
        public static void WorkFunction(object n)
        {
            Console.Write(n + "\t");
          
        }
      
       
    }
}

启动程序: 

当需要处理返回值或执行异步操作时,应该考虑使用 Task 或 async/await 关键字 

创建一个返回 Task<TResult> 的方法,其中 TResult 是你期望的返回类型。然后,你可以使用 Task.Run 方法来在线程池上异步执行这个任务,并等待它完成以获取结果。 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XianChengYiBu
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            string n = "Hello";
            int i = 123;
            // 使用 Task.Run 和 lambda 表达式来在线程池上异步执行 WorkFunctionAsync  
            Task<int> task = Task.Run(() => WorkFunctionAsync(n, i));

            // 等待任务完成并获取结果  
            int result = await task;



            // int result = await WorkFunctionAsync("SomeString", 123);// 调用 WorkFunctionAsync 并等待结果  
            Console.WriteLine("Result: " + result);
            Console.ReadKey();
        }

        //-----------------------   ------------------------------//
        public static async Task<int> WorkFunctionAsync(string n, int i)//Task<int>,int是返回值
        {
            // 模拟一些工作  
            Console.Write(n + "\t" + i + "\n");
            // 假设这是某个计算的结果  
            int result = i * 2;
            return result;
        }

    }
}

 启动程序:

 

 ThreadPool 可以看做容纳线程的容器,我们可通过QueueUserWorkItem()方法把工作函 数排入线程池。

在上面的程序中,我们向线程池中排入了100个工作函数,线程池分别独立的完成了 这100个任务。下面我们研究一下 线程池运行过程中线程数目的变化情况,从而加深对线程池的理解。为了叙述方便,我们 假设下限为10,上限为30。

1.当线程池被创建后,里面就会创建10个空线程(和下限值相同)。 
2.当我们向线程池中排入一个任务后,就会有一个空线程接手该任务,然后运行起
来。随着我们不断向线程池中排入任务,线程池中的空线程逐一运行起来。
3.随着任务的不断增加,在某一时刻任务数量会超出下限,这时线程的数量就不够
用了,但线程池并不会立即创建新线程,而是等待500毫秒左右,这么做的目的是看看在
这段时间内是否有其他线程完成任务并接手这个请求,这样就可以避免因创建新线程而造
成的消耗。如果这段时间内没有线程完成任务,就创建一个新线程去执行新任务。 
4.在任务数量超过下限后,随着新任务的不断排入,线程池中线程数量持续增加,
直至达到上限值为止。 
5.当线程数量达到上限时,继续增加任务,线程数量将不再增加。比如你向线程池
中排入100个任务,则只有30个进入线程池(和上限相同),另外70个在线程池外排队
等待。当线程池中的某个线程完成任务后,并不会立即终止,而是从等待队列中选择一个
任务继续执行,这样就减少了因创建和销毁线程而消耗的时间。 
6.随着任务逐步完成,线程池外部等候的任务被逐步调入线程池,任务的数量逐步
减少,但线程的总数保持恒定,始终为30(和上限值相同)。 
7.随着任务的逐渐减少,总有某一时刻,任务数量会小于上限值,这时线程池内多
余的线程会在空闲2分钟后被释放并回收相关资源。线程数目逐步减少,直到达到下限值
为止。
8.当任务数量减小到下限值之下时,线程池中的线程数目保持不变(始终和下限值
相同),其中一部分在执行任务,另一部分处于空运行状态。 
9.当所有任务都完成后,线程池恢复初始状态,运行10个空线程。 
由上面的论述可以看出线程池提高效率的关键是一个线程完成任务后可以继续为其
他任务服务,这样就可以使用有限的几个固定线程轮流为大量的任务服务,从而减少了因
频繁创建和销毁线程所造成的消耗。 
可见线程池适用于包含包含大量简单线程且这些线程不需要特殊控制的程序,它不但
简单快捷,而且所耗费的系统开销远比Thread少。 
ThreadPool 中的线程不用手动开始,也不能手动取消,你要做的只是把工作函数排入
线程池,剩下的工作将由系统自动完成,也就是说我们不能控制线程池中的线程。如果想
对线程进行更多的控制,那么就不适合使用线程池。在以下情况中不宜使用 ThreadPool
类而应该使用单独的Thread类: 
1. 线程执行需要很长时间(如果有些线程长期占用线程池,那么对在外面排队的任
务说就是灾难); 
2. 需要为线程指定详细的优先级;
3. 在执行过程中需要对线程进行操作,比如睡眠,挂起等。
所以ThreadPool 适合于并发运行若干个运行时间不长且互不干扰的函数。
 
 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1680930.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用一段传输线表示电感和电容

文中部分图片来自于《complete Wireless design》 如何使用一段传输线来表示电感和电容&#xff0c;本文将就此内容展开&#xff1a;

Redis-如何保证与Mysql数据一致性

文章目录 Redis与Mysql数据一致性的情况有哪些&#xff1f;Redis与Mysql数据保持一致性的方案&#xff1f;同步双写机制删除缓存重新加载机制延迟双删机制利用MQ保持数据一致性 本篇小结 更多相关内容可查看 Redis与Mysql数据一致性的情况有哪些&#xff1f; Redis和MySQL是两…

vue3 自定义组件

在项目中&#xff0c;我们会遇到一些没有现成的组件&#xff0c;那这个时候我们就需要自己去写一个满足我们需求的组件。 比如&#xff0c;我需要一个上下排布&#xff0c;上面显示标题&#xff0c;下面显示内容的组件。封装完成后方便复用。 1、布局组件 我定义一个上下结构的…

Git使用(4):分支管理

一、新建分支 首先选择Git -> Branches... 然后选择 New Branch&#xff0c;输入新分支名称&#xff0c;例如dev。 可以看到右下角显示已经切换到新建的dev分支了。 push到远程仓库&#xff0c;可以看到新添加的分支。 二、切换分支与合并分支 为了演示合并分支&#xff0c…

Linux(七) 动静态库

目录 一、动静态库的概念 二、静态库的打包与使用 2.1 静态库的打包 2.2 静态库的使用 三、动态库的打包与使用 3.1 动态库的打包 3.2 动态库的使用 3.3 运行动态库的四种方法 四、总makefile 一、动静态库的概念 静态库&#xff1a; Linux下&#xff0c;以.a为后缀的…

Python——IO编程

IO在计算机中指Input/Output&#xff0c;也就是输入和输出。由于程序和运行时数据是在内存中驻留&#xff0c;由CPU这个超快的计算核心来执行&#xff0c;涉及到数据交换的地方&#xff0c;通常是磁盘、网络等&#xff0c;就需要IO接口。 比如你打开浏览器&#xff0c;访问新浪…

蓝桥杯 EDA 组 历届国赛真题解析

一、2021年国赛真题 1.1 CN3767 太阳能充电电路 CN3767 是具有太阳能电池最大功率点跟踪功能的 4A&#xff0c;12V 铅酸电池充电管理集成电路。 最大功率点应指的是电池板的输出电压&#xff0c;跟踪电压其做保护。当然 CN3767 也可以直接使用直流充电&#xff0c;具体可以阅读…

污水处理环保设备厂商怎么选

在选择污水处理环保设备厂商时&#xff0c;需要综合考虑多个因素来确保选取的供应商能够提供高质量的设备和服务。以下是一些主要的考虑因素&#xff1a; 企业资质和认证&#xff1a;首先检查供应商是否拥有相关的资质证书和行业认证&#xff0c;例如ISO 9001质量管理体系认证、…

0基础安装 composer

解决&#xff1a; composer 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 php composer.phar可以运行 安装环境&#xff1a;系统w11 官网地址&#xff1a;Composer 1.安装composer 1.1打开命令行窗口 在命令行窗口里&#xff0c;右键是粘贴&#xff0…

Java | Leetcode Java题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {// 设置 dummyNode 是这一类问题的一般做法ListNode dummyNode new ListNode(-1);dummyNode.next head;ListNode pre dummyNode;for (int i 0; …

【数组的度】leetcode,python

一种很菜的做法&#xff08;暴力&#xff09;&#xff0c;for循环&#xff08;样例能过一大半呢&#xff0c;复杂度的话。。。&#xff09; class Solution:def findShortestSubArray(self, nums: List[int]) -> int:nlen(nums)if n1:return nmx1#出现次数最多的计算for i …

Google:站长移除无效网址

当您的网址不需要呈现在Google站长中时&#xff0c;您可以在站长工具中移除网址 操作步骤&#xff1a;登录Google站长&#xff0c;绑定网站完成后&#xff0c;点击左侧删除 >> 输入网址 如果遇到一些网址&#xff0c;可以找寻网址间的规律&#xff0c;比如说&#xff0…

【oracle】图片转为字节、base64编码等形式批量插入oracle数据库并查询

1.熟悉、梳理、总结下Oracle相关知识体系 2.欢迎批评指正&#xff0c;跪谢一键三连&#xff01; 资源下载&#xff1a; oci.dll、oraocci11.dll、oraociei11.dll3个资源文件资源下载&#xff1a; Instant Client Setup.exe资源下载&#xff1a; oci.dll、oraocci11.dll、oraoc…

java代码混淆工具ProGuard混淆插件

java代码混淆工具ProGuard混淆插件 介绍 ProGuard是一个纯java编写的混淆工具&#xff0c;有客户端跟jar包两种使用方式。可以将程序打包为jar&#xff0c;然后用工具进行混淆&#xff0c;也可以在maven中导入ProGuard的插件&#xff0c;对代码进行混淆。 大家都知道 java代…

【SQL】SQL常见面试题总结(1)

目录 1、检索数据1.1、从 Customers 表中检索所有的 ID1.2、检索并列出已订购产品的清单1.2、检索所有列 2、排序检索数据2.1、检索顾客名称并且排序2.2、对顾客 ID 和日期排序2.3、按照数量和价格排序2.4、检查 SQL 语句 3、过滤数据3.1、返回固定价格的产品3.2、返回产品并且…

基于Vue和uni-app的增强型单选ccRadioView组件开发

标题&#xff1a;基于Vue和uni-app的增强单选组件ccRadioView的设计与实现 摘要&#xff1a;本文将详细介绍如何使用Vue和uni-app构建一个简单、好用且通用的单选框组件ccRadioView。该组件提供了单选列表的功能&#xff0c;并支持反向传值&#xff0c;方便开发者快速实现单选…

新人学习笔记值(初始JavaScript)

一、Java Script是什么 1.Java Script是世界上最流行的语言之一&#xff0c;是一种运行在客户端的脚本语言&#xff08;script是脚本的意思&#xff09; 2.脚本语言&#xff1a;不需要编译&#xff0c;运行过程中由js解释器&#xff08;js引擎&#xff09;进行解释并运行 3.现在…

如何同步管理1000个设备的VLAN数据?

什么是VLAN&#xff1f; VLAN&#xff0c;也就是虚拟局域网&#xff0c;是通过为子网提供数据链路连接来抽象出局域网的概念。在企业网中&#xff0c;一个企业级交换机一般是24口或者是48口&#xff0c;连接这些接口的终端在物理上形成一个广播域。广播域过大&#xff0c;就会导…

docker镜像中搭建FastDfs

docker镜像中搭建FastDfs 一、搭建过程二、docker端口映射三、映射的方法三、配置Tracker 和 Storage 环境&#xff1a;腾讯云服务器上 ubuntu20.04镜像 一、搭建过程 正常直接在云服务器上搭建过程参考博客&#xff1a; https://blog.csdn.net/qq_38531706/article/details/…

基于51单片机的AD/DA转换的串口通信proteus仿真(附源码)

文章目录 一、前言二、PCF85911.介绍2.原理图3.引脚介绍 三、仿真图1.未仿真时2.仿真时 四、仿真程序main.cIIC.c 五、总结 一、前言 AT89C52是一款经典的8051系列单片机&#xff0c;它通常不包含内置的模数转换器&#xff08;ADC&#xff09;或数字模拟转换器&#xff08;DAC…