当前位置: 首页 > news >正文

C#中的线程池使用(一)

1  线程池的概念

许多应用程序使用多个线程,但这些线程经常在休眠状态中耗费大量的时间来等待事件发生。其他线程可能进入休眠状态,并且仅定期被唤醒以轮询更改或更新状态信息,然后再次进入休眠状态。为了简化对这些线程的管理,.NET框架为每一个进程提供了一个线程池,使应用程序能够根据需要来有效地利用多个线程。一个线程监视排到线程池的若干个等待操作的状态。当一个等待操作完成时,线程池中的一个辅助线程就会执行对应的回调函数。线程池中的线程由系统进行管理,程序员不需要费力于线程管理,可以集中精力处理应用程序任务。

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间之后创建另一个辅助线程。但线程的数目永远不会超过最大值。超过最大值的其他线程可以排队,但它们要等到其他线程完成后才启动。

2  线程池的应用范围

线程池特别适合于执行一些需要多个线程的任务。使用线程池能够优化这些任务的执行过程,从而提高吞吐量,它不仅能够使系统针对此进程优化该执行过程,而且还能够使系统针对计算机上的其他进程优化该执行过程。如果需要启动多个不同的任务,而不想分别设置每个线程的属性,则可以使用线程池。

如果应用程序需要对线程进行特定的控制,则不适合使用线程池,需要创建并管理自己的线程。

在以下几种情况下,适合于使用线程池线程:

(1)不需要前台执行的线程。

(2)不需要在使用线程具有特定的优先级。

(3)线程的执行时间不易过长,否则会使线程阻塞。由于线程池具有最大线程数限制,因此大量阻塞的线程池线程可能会阻止任务启动。

(4)不需要将线程放入单线程单元。所有 ThreadPool 线程均不处于多线程单元中。

(5)不需要具有与线程关联的稳定标识,或使某一线程专用于某一任务。

3  ThreadPool类

在多线程的程序中,经常会出现两种情况:一种是在应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应,这一般使用ThreadPool(线程池)来解决;另一种情况是在线程平时都处于休眠状态,只是周期性地被唤醒,这一般使用Timer(定时器)来解决。下面对ThreadPool类进行详细说明。

ThreadPool类提供一个线程池,该线程池可用于发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。该类提供一个由系统维护的线程池(可以看作一个线程的容器),该容器需要Windows 2000以上系统支持,因为其中某些方法调用了只有高版本的Windows才有的API函数。

下面介绍一下该类所提供的方法,如表1所示。

表1                          ThreadPool类的方法

方法

描述

BindHandle

将操作系统句柄绑定到ThreadPool

GetAvailableThreads

检索由GetMaxThreads方法返回的最大线程池线程数和当前活动线程数之间的差值

GetMaxThreads

检索可以同时处于活动状态的线程池请求的数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用

GetMinThreads

检索线程池在新请求预测中维护的空闲线程数

QueueUserWorkItem

将方法排入队列以便执行。此方法在有线程池线程变得可用时执行

RegisterWaitForSingleObject

注册正在等待WaitHandle的委托

SetMaxThreads

设置可以同时处于活动状态的线程池的请求数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用

SetMinThreads

设置线程池在新请求预测中维护的空闲线程数

UnsafeQueueNativeOverlapped

将重叠的 I/O 操作排队以便执行

UnsafeQueueUserWorkItem

注册一个等待 WaitHandle 的委托

UnsafeRegisterWaitForSingleObject

将指定的委托排队到线程池

通过以上方法,可以对线程池进行设置以及相应的操作,那么,我们什么时候使用线程池呢?我们在用Thread类调用线程时,一次只能使用一个线程来创建和删除线程,这种方式的建立和删除线程对CPU的使用是很频繁的,为了节省CPU的负荷,可以使用线程池对线程进行操作,为了使读者更深入的了解Thread类与ThreadPool类的差别。

在以下情况下,应使用ThreadPool类:

  1. 要以最简单的方式创建和删除线程;
  2. 应用程序使用线程的性能要优先考虑。
  3. 在以下情况下,应使用Thread类:
  4. 要控制所创建线程的优先级;
  5. 希望所使用的线程维护其标识,该标识要与线程一起进行各种操作,经过许多不同的时间段;
  6. 所使用的线程的寿命较长。

ThreadPool类会在线程的托管池中重用已有的线程。使用完线程后,线程就会返回线程池,供以后使用。ThreadPool有25个可用的线程(每个处理器)。

在使用线程池时,一般调用ThreadPool类的QueueUserWorkItem方法。

用户并不需要自已建立线程,只需要把要进行的操作写成函数,然后作为参数传递给ThreadPool类的QueueUserWorkItem方法就行了,传递的方法是依靠WaitCallback代理对象,而线程的建立、管理、运行等工作都是由系统自动完成的,用户无须考虑那些复杂的细节问题。ThreadPool类的用法:首先程序创建了一个ManualResetEvent对象,该对象就像一个信号灯,可以利用它的信号来通知其它线程。

4  线程池的设置

为了读者能更好的控制线程池,下面讲解一下如何设置和读取线程池的最大线程数和最小空闲线程数。

1.线程池的最大线程数

可排队到线程池的操作数仅受可用内存的限制;但是,线程池限制进程中可以同时处于活动状态的线程数。默认情况下,限制每个CPU可以使用25个辅助线程和1000 个I/O完成线程。

通过使用GetMaxThreads和SetMaxThreads方法可以控制最大线程数。下面对这两个方法的声明进行一下说明。

(1)GetMaxThreads方法

该方法检索可以同时处于活动状态的线程池请求的数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。其语法如下:

public static void GetMaxThreads(out int workerThreads,out int completionPortThreads)

参数说明:

workerThreads:Int32,线程池中辅助线程的最大数目。

CompletionPortThreads:Int32,线程池中异步I/O线程的最大数目。

(2)SetMaxThreads方法

该方法设置可以同时处于活动状态的线程池的请求数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。

[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]

public static bool SetMaxThreads(int workerThreads,int completionPortThreads)

参数说明:

workerThreads:Int32,线程池中辅助线程的最大数目。

CompletionPortThreads:Int32,线程池中异步 I/O 线程的最大数目。

返回值:bool型,如果更改成功,则为true;否则为false。

下面通过一段代码对GetMaxThreads和SetMaxThreads方法的应用进行一下讲解,主要是显示线程池中辅助线程的最大数和线程池中异步I/O线程的最大数,以及用户自行设置后的线程池情况。代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            //设置正在等待线程的事件为终止

            AutoResetEvent autoEvent = new AutoResetEvent(false);

            int workerThreads;

            int portThreads;

            //获取处于活动状态的线程池请求的数目

            ThreadPool.GetMaxThreads(out workerThreads, out portThreads);

            //在控制台中显示处于活动状态的线程池请求的数目

Console.WriteLine("设置前,线程池中辅助线程的最大数为:" + workerThreads.ToString() + ";线程池中异步I/O线程的最大数为:" + portThreads.ToString());

            workerThreads = 10;//设置辅助线程的最大数

            portThreads = 500;//设置线程池中异步I/O线程的最大数

            //设置处于活动状态的线程池请求的数目

            ThreadPool.SetMaxThreads(workerThreads, portThreads);

            ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod), autoEvent);//执行线程池

            //在控制台中显示设置后的处于活动状态的线程池请求的数目

            Console.WriteLine("设置后,线程池中辅助线程的最大数为:" + workerThreads.ToString() + ";线程池中异步I/O线程的最大数为:" + portThreads.ToString());

            Console.ReadLine();

        }

 

        static void ThreadMethod(object stateInfo)

        {

            Console.WriteLine("执行线程池");

        }

      }

}

运行结果如下:

注意在 .NET Framework 1.0 和 1.1 版中,不能从托管代码中设置线程池大小。承载公共语言运行库的代码可以使用 mscoree.h 中定义的 CorSetMaxThreads 设置该大小。

2.线程池的最小空闲线程数

即使是在所有线程都处于空闲状态时,线程池也会维持最小的可用线程数,以便队列任务可以立即启动。将终止超过此最小数目的空闲线程,以节省系统资源。默认情况下,每个处理器维持一个空闲线程。

在启动新的空闲线程之前,线程池具有一个内置延迟(在 .NET Framework 2.0 版中为半秒钟)。应用程序在短期内定期启动许多任务时,空闲线程数的微小增加会导致吞吐量显著增加。将空闲线程数设置得过高会浪费系统资源。

使用GetMinThreads和SetMinThreads方法可以控制线程池所维持的空闲线程数。下面对这两个方法进行一下说明。

(1)GetMinThreads方法

该方法用于检索线程池在新请求预测中维护的空闲线程数。其语法如下:

public static void GetMinThreads(out int workerThreads,out int completionPortThreads)

参数说明:

workerThreads:Int32,当前由线程池维护的空闲辅助线程的最小数目。

CompletionPortThreads:Int32,当前由线程池维护的空闲异步I/O线程的最小数目。

(2)SetMinThreads方法

该方法用于设置线程池在新请求预测中维护的空闲线程数。其语法如下:

[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]

public static bool SetMinThreads(int workerThreads,int completionPortThreads)

参数说明:

workerThreads:类型:Int32,要由线程池维护的新的最小空闲辅助线程数。

CompletionPortThreads:Int32,要由线程池维护的新的最小空闲异步 I/O 线程数。

返回值:bool型,如果更改成功,则为 true;否则为 false。

下面用一段代码来详细说明一下GetMinThreads和SetMinThreads方法的应用,代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            int minWorker, minIOC;

            //获取线程池在新请求预测中维护的默认空闲线程数

            ThreadPool.GetMinThreads(out minWorker, out minIOC);

            Console.WriteLine("设置前,线程池维护的空闲辅助线程的最小数目为:" + minWorker.ToString() + ";线程池维护的空闲异步I/O线程的最小数目为:" + minIOC.ToString());//在控制台中显示线程池的默认空闲线程数

            minWorker = 4;//设置线程池维护的空闲辅助线程的最小数

            minIOC = 10;//设置线程池维护的空闲异步I/O线程的最小数

            if (ThreadPool.SetMinThreads(minWorker, minIOC))//如果更改成功

            {

                Console.WriteLine("设置后,线程池维护的空闲辅助线程的最小数目为:" + minWorker.ToString() + ";线程池维护的空闲异步I/O线程的最小数目为:" + minIOC.ToString());//在控制台中显示更改后的线程池的默认空闲线程数

            }

            else

            {

                Console.WriteLine("没有设置");

            }

            Console.ReadLine();

        }

    }

}

运行结果如下:

注意:在.NET Framework 1.0版中,不能设置最小空闲线程数。

转载于:https://www.cnblogs.com/DonetRen/p/10167095.html

相关文章:

  • android实现图片识别的几种方法
  • 12月24日云栖精选夜读 | 刚刚,阿里开源首个深度学习框架 X-Deep Learning!
  • 微信智能开放平台
  • SqlServer如何获取存储过程的返回值
  • MyEclipse2014配置Tomcat开发JavaWeb程序JSP以及Servlet http://my.csdn.net/21aspnet/message CSDN Five...
  • 梯度下降
  • ES7 ES8 的新特性(部分)
  • Android程序完全退出
  • P2-2017级算法第二次上机 B 女娲加农炮
  • 很好的学习平台
  • 前言-如何学习区块链
  • 移除孤立的或已删除的链接克隆
  • Windows 8.1 Windows Phone 8.1 通用程序
  • HowTos/Virtualization/VirtualBox - CentOS Wiki
  • MySQL 运维笔记(一)—— 终止高负载SQL
  • [PHP内核探索]PHP中的哈希表
  • 「译」Node.js Streams 基础
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • Angular数据绑定机制
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • leetcode98. Validate Binary Search Tree
  • magento2项目上线注意事项
  • oschina
  • Swift 中的尾递归和蹦床
  • Vue UI框架库开发介绍
  • 从零开始在ubuntu上搭建node开发环境
  • 你真的知道 == 和 equals 的区别吗?
  • 前端技术周刊 2019-01-14:客户端存储
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 微信小程序:实现悬浮返回和分享按钮
  • 物联网链路协议
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 找一份好的前端工作,起点很重要
  • Android开发者必备:推荐一款助力开发的开源APP
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • (10)ATF MMU转换表
  • (C++17) optional的使用
  • (Java数据结构)ArrayList
  • (安卓)跳转应用市场APP详情页的方式
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (南京观海微电子)——I3C协议介绍
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .net php 通信,flash与asp/php/asp.net通信的方法
  • .NET 设计模式初探
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [2017][note]基于空间交叉相位调制的两个连续波在few layer铋Bi中的全光switch——
  • [AIGC] Nacos:一个简单 yet powerful 的配置中心和服务注册中心
  • [AR Foundation] 人脸检测的流程