C#多线程之线程池ThreadPool详解

作者:springsnow 时间:2021-10-30 23:59:45 

一、ThreadPool概述

提供一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。

创建线程需要时间。如果有不同的小任务要完成,就可以事先创建许多线程/在应完成这些任务时发出请求。不需要自己创建这样一个列表。该列表由ThreadPool类托管。

这个类会在需要时增减池中线程的线程数,直到最大的线程数。池中的最大线程数是可配置的。在双核CPU中,默认设置为1023 个工作线程和1000个I/O线程。也可以指定在创建线程池时应立即启动的最小线程数,以及线程池 中可用的最大线程数。

如果有更多的作业要处理,线程池中线程的个数也达到了极限,最新的作业就要排队,且必须等待线程完成其任务。

线程池使用起来很简单,但它有一些限制:

  • 线程池中的所有线程都是后台线程。如果进程的所有前台线程都结束了,所有的后台线程 就会停止。不能把入池的线程改为前台线程。

  • 不能给入池的线程设置优先级或名称。

  • 对于COM对象,入池的所有线程都是多线程单元(multithreaded apartment, MTA)线程。许 多COM对象都需要单线程单元(single-threaded apartment, MTA)线程。

  • 入池的线程只能用于时间较短的任务。如果线程要一直运行(如Word的拼写检杳器线程), 就应使用Thread类创建一个线程.

使用线程池线程的操作的情况包括:

  • 当您创建Task或Task<TResult>对象以异步方式执行某项任务,默认情况下任务调度在线程池线程上运行的。

  • 异步计时器使用线程池。 线程池线程从System.Threading.Timer类执行回调,和从System.Timers.Timer类引发事件。

  • 当使用已注册的等待句柄时,系统线程监视等待句柄的状态。 等待操作完成后,从线程池的工作线程将执行相应的回调函数。

  • 当您调用QueueUserWorkItem方法进行排队,以在线程池线程上执行的方法。 为此,可将该方法传递WaitCallback委托。

二、方法

  • GetAvailableThreads(Int32, Int32)
    检索由 GetMaxThreads(Int32, Int32) 方法返回的最大线程池线程数和当前活动线程数之间的差值。

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

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

  • GetMinThreads(Int32, Int32)
    发出新的请求时,在切换到管理线程创建和销毁的算法之前检索线程池按需创建的线程的最小数量。

  • SetMinThreads(Int32, Int32)
    发出新的请求时,在切换到管理线程创建和销毁的算法之前设置线程池按需创建的线程的最小数量。

  • QueueUserWorkItem(WaitCallback, Object)
    将方法排入队列以便执行,并指定包含该方法所用数据的对象。 此方法在有线程池线程变得可用时执行。

  • RegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, Object, Int32, Boolean)
    注册一个等待 WaitHandle 的委托,并指定一个 32 位有符号整数来表示超时值(以毫秒为单位)。

三、获取线程数方法

int i = 0;
int j = 0;
//前面是辅助(也就是所谓的工作者)线程,后面是I/O线程
ThreadPool.GetMaxThreads(out i, out j);
Console.WriteLine(i.ToString() + "   " + j.ToString()); //默认都是1000

//获取空闲线程,由于现在没有使用异步线程,所以为空
ThreadPool.GetAvailableThreads(out i, out j);
Console.WriteLine(i.ToString() + "   " + j.ToString()); //默认都是1000

四、QueueUserWorkItem(WaitCallback, Object)

将方法排入队列以便执行,并指定包含该方法所用数据的对象。 此方法在有线程池线程变得可用时执行。

public static bool QueueUserWorkItem (System.Threading.WaitCallback callBack, object state);

实例:

static void Main(string[] args)
{
    Person p = new Person(1, "刘备");
    //启动工作者线程
   ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread), p);
}

static void RunWorkerThread(object obj)
{
    Thread.Sleep(200);
    Console.WriteLine("线程池线程开始!");
    Person p = obj as Person;
    Console.WriteLine(p.Name);
}

public class Person
{
    public Person(int id, string name) { Id = id; Name = name; }
    public int Id { get; set; }
    public string Name { get; set; }
}

五、RegisterWaitForSingleObject 注册等待句柄

注册一个等待 WaitHandle 的委托,并指定一个数来表示超时值(以毫秒为单位)。

将指定的方法排队到线程池,当超时或者等待委托接收到信号时,辅助线程将执行此方法,即主线程控制辅助线程什么时候开始执行。

public static System.Threading.RegisteredWaitHandle RegisterWaitForSingleObject (System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object state, int millisecondsTimeOutInterval, bool executeOnlyOnce);

参数

  • waitObject
    要注册的 WaitHandle。 使用 WaitHandle 而非 Mutex。

  • callBack
    向 waitObject 参数发出信号时调用的 WaitOrTimerCallback 委托。

  • state
    传递给委托的对象。

  • millisecondsTimeOutInterval
    以毫秒为单位的超时。 如果 millisecondsTimeOutInterval 参数为 0(零),函数将测试对象的状态并立即返回。 如果 millisecondsTimeOutInterval 为 -1,则函数的超时间隔永远不过期。
    表示间隔几秒执行回调方法,指当刚加入线程后,它是需要过了几秒后才会第一次执行回调方法。如果使用了wait.Set()方法使用立即执行回调函数而不需要等待。

  • executeOnlyOnce
    如果为 true,表示在调用了委托后,线程将不再在 waitObject 参数上等待;如果为 false,表示每次完成等待操作后都重置计时器,直到注销等待。

返回

  • RegisteredWaitHandle
    封装本机句柄的 RegisteredWaitHandle。

// TaskInfo contains data that will be passed to the callback method.
public class TaskInfo
{
   public RegisteredWaitHandle Handle = null;
   public string OtherInfo = "default";
}

public static void Main(string[] args)
{
   // 主线程使用AutoResetEvent来给已注册的等待句柄发信号, 此等待句柄执行回调方法
   AutoResetEvent ev = new AutoResetEvent(false);

TaskInfo ti = new TaskInfo();
   ti.OtherInfo = "First task";
   // The TaskInfo for the task includes the registered wait handle returned by RegisterWaitForSingleObject.  This
   // allows the wait to be terminated when the object has been signaled once (see WaitProc).
   ti.Handle = ThreadPool.RegisterWaitForSingleObject(
       ev,
       new WaitOrTimerCallback(WaitProc),
       ti,
       1000,
       false
   );

// 主线程等待三秒,为了演示队列中的线程超时,然后发信号.
   Thread.Sleep(3100);
   Console.WriteLine("Main thread signals.");
   ev.Set();//发信号

// The main thread sleeps, which should give the callback method time to execute.  If you comment out this line, the program usually ends before the ThreadPool thread can execute.
   Thread.Sleep(1000);
   // If you start a thread yourself, you can wait for it to end by calling Thread.Join.  This option is not available with  thread pool threads.
}

//The callback method executes when the registered wait times out,
//or when the WaitHandle (in this case AutoResetEvent) is signaled.
//WaitProc unregisters the WaitHandle the first time the event is  signaled.
public static void WaitProc(object state, bool timedOut)
{
   TaskInfo ti = (TaskInfo)state;

string cause = "TIMED OUT";
   if (!timedOut) //如果Timeout为false,表示接收到的信号后执行的
   {
       cause = "SIGNALED";
       //如果回调方法执行的话是因为WaitHandle触发信号的话,则用反注册等待句柄来取消回调方法将来的执行。
       if (ti.Handle != null)
           ti.Handle.Unregister(null);//
   }

Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
       ti.OtherInfo, Thread.CurrentThread.GetHashCode().ToString(), cause);//超时后执行的
}

结果如下:

WaitProc( First task ) executes on thread 7; cause = TIMED OUT.
WaitProc( First task ) executes on thread 7; cause = TIMED OUT.
WaitProc( First task ) executes on thread 7; cause = TIMED OUT.
Main thread signals.
WaitProc( First task ) executes on thread 7; cause = SIGNALED.

来源:https://www.cnblogs.com/springsnow/p/9428805.html

标签:C#,多线程,线程池,ThreadPool
0
投稿

猜你喜欢

  • Android事件分发机制(上) ViewGroup的事件分发

    2022-05-28 12:40:05
  • SpringCloud Zuul基本使用方法汇总

    2023-11-14 15:21:54
  • 简单掌握Android开发中彩信的发送接收及其附件的处理

    2023-04-18 02:32:24
  • 分享Android仿刮奖效果控件

    2021-12-15 16:00:16
  • Python调用Java可执行jar包问题

    2021-06-02 10:26:26
  • java实现哈弗曼编码与反编码实例分享(哈弗曼算法)

    2023-11-25 04:54:05
  • C#同步、异步远程下载文件实例

    2023-08-26 21:08:49
  • 使用Jacoco获取 Java 程序的代码执行覆盖率的步骤详解

    2022-07-22 00:25:13
  • C#远程发送和接收数据流生成图片的方法

    2021-08-31 00:30:10
  • Elasticsearch查询及聚合类DSL语句宝典示例详解

    2023-01-10 18:51:46
  • SpringBoot整合Mybatis实现多数据源配置与跨数据源事务实例

    2023-06-29 23:47:34
  • Java框架解说之BIO NIO AIO不同IO模型演进之路

    2021-06-20 22:56:31
  • Java学习基础之安装JDK/配置JDK环境&IEDA工具安装

    2023-02-09 13:02:40
  • Intellij IDEA如何去掉@Autowired 注入警告的方法

    2023-02-13 04:34:49
  • 一文带你了解如何正确使用Java中的字符串常量池

    2022-07-13 01:35:30
  • C#实现线程安全的简易日志记录方法

    2023-12-20 22:45:17
  • 详解Spring MVC CORS 跨域

    2023-11-25 08:04:37
  • C#使用timer实现的简单闹钟程序

    2023-07-30 00:24:28
  • Java Validation方法入参校验实现过程解析

    2021-08-04 03:31:50
  • Android多线程+单线程+断点续传+进度条显示下载功能

    2023-08-13 15:48:26
  • asp之家 软件编程 m.aspxhome.com