5天不再惧怕多线程——第五天 线程池

简介:

说到多线程,不可不说线程池,C#中关于池的概念很多,今天来整理下ThreadPool的使用。

是的,如果你很懒,如果你的执行任务比较短,如果你不想对线程做更精细的控制,那么把这些繁琐的东西丢给线程池吧。

一:ThreadPool

好了,下面看看TheadPool下有哪些常用的方法。

1:GetMaxThreads,GetMinThreads

首先我们肯定好奇线程池到底给我们如何控制线程数,下面就具体的看一看。

class Program
 {
 static void Main(string[] args)
 {
 int workerThreads;

 int completePortsThreads;

 ThreadPool.GetMaxThreads(out workerThreads, out completePortsThreads);

 Console.WriteLine("线程池中最大的线程数{0},线程池中异步IO线程的最大数目{1}", workerThreads, completePortsThreads);

 ThreadPool.GetMinThreads(out workerThreads, out completePortsThreads);

 Console.WriteLine("线程池中最小的线程数{0},线程池中异步IO线程的最小数目{1}", workerThreads, completePortsThreads);
 }
 }

有的同学可能就要问,我可以将1023设置为10230吗?那么就会有10230个线程帮我做事多好啊,其实不然。

①:我先前的文章也说过,线程很多的话,线程调度就越频繁,可能就会出现某个任务执行的时间比线程调度花费的时间短很多的尴尬局面。

②:我们要知道一个线程默认占用1M的堆栈空间,如果10230个线程将会占用差不多10G的内存空间,我想普通的电脑立马罢工。



2:SetMaxTheads,SetMinThreads

当然,默认的线程设置只是一个参考,如果我们处于性能和实际情况确实需要修改也没关系,framework也给我们提供了现成的方法。

class Program
 {
 static void Main(string[] args)
 {
 int workerThreads;

 int completePortsThreads;
 
 ThreadPool.SetMaxThreads(100, 50);

 ThreadPool.SetMinThreads(20, 10);

 ThreadPool.GetMaxThreads(out workerThreads, out completePortsThreads);

 Console.WriteLine("线程池中最大的线程数{0},线程池中异步IO线程的最大数目{1}\n", workerThreads, completePortsThreads);

 ThreadPool.GetMinThreads(out workerThreads, out completePortsThreads);

 Console.WriteLine("线程池中最小的线程数{0},线程池中异步IO线程的最小数目{1}\n", workerThreads, completePortsThreads);
 }
 }


3: QueueUserWorkItem

需要容纳任务并执行的方法来了,该方法有一个WaitCallBack的委托,我们只需要把将要执行的任务丢给委托,CLR将会在线程池中调派空闲的

线程执行。

namespace ConsoleApplication3
{
 class Program
 {
 static void Main(string[] args)
 {
 ThreadPool.QueueUserWorkItem(Run1);

 Console.Read();
 }

 static void Run1(object obj)
 {
 Console.WriteLine("我是线程{0},我是线程池中的线程吗? \n回答:{1}", Thread.CurrentThread.ManagedThreadId,
 Thread.CurrentThread.IsThreadPoolThread);
 }
 }
}

可能我们也需要像普通的Thread一样带一些参数到工作线程中,QueueUserWorkItem的第二个重载版本解决了我们的问题。

class Program
 {
 static void Main(string[] args)
 {
 ThreadPool.QueueUserWorkItem(Run1, "我是主线程");

 Console.Read();
 }

 static void Run1(object obj)
 {
 Console.WriteLine(obj);
 }
 }


4:RegisterWaitForSingleObject

我们知道,如果我们把要执行的任务丢给线程池后,相当于把自己的命运寄托在别人的手上。

①:我们再也不能控制线程的优先级了。

②:丢给线程池后,我们再也不能将要执行的任务取消了。

是的,给别人就要遵守别人的游戏规则,不过RegisterWaitForSingleObject提供了一些简单的线程间交互,因为该方法的第一个参数是

WaitHandle,在VS对象浏览器中,我们发现EventWaitHandle继承了WaitHandle,而ManualResetEvent和AutoResetEvent都继承于

EventWaitHandle,也就是说我们可以在RegisterWaitForSingleObject溶于信号量的概念。


class Program
 {
 static void Main(string[] args)
 {
 AutoResetEvent ar = new AutoResetEvent(false);

 ThreadPool.RegisterWaitForSingleObject(ar, Run1, null, Timeout.Infinite, false);

 Console.WriteLine("时间:{0} 工作线程请注意,您需要等待5s才能执行。\n", DateTime.Now);

 //5s
 Thread.Sleep(5000);

 ar.Set();

 Console.WriteLine("时间:{0} 工作线程已执行。\n", DateTime.Now);

 Console.Read();
 }

 static void Run1(object obj, bool sign)
 {
 Console.WriteLine("当前时间:{0} 我是线程{1}\n", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
 }
 }

我们知道在Threading下面有一个Timer计时器,当定期触发任务的时候都是由线程池提供并给予执行,那么这里我们溶于信号量的概念以后同样

可以实现计时器的功能。


class Program
 {
 static void Main(string[] args)
 {
 AutoResetEvent ar = new AutoResetEvent(false);

 //参数2000:其实就是WaitOne(2000),采取超时机制
 ThreadPool.RegisterWaitForSingleObject(ar, Run1, null, 2000, false);

 Console.Read();
 }

 static void Run1(object obj, bool sign)
 {
 Console.WriteLine("当前时间:{0} 我是线程{1}\n", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
 }
 }
}

有时候,跑着跑着我们需要在某个时刻停止它,没关系,RegisterWaitForSingleObject返回一个RegisteredWaitHandle类,那么我们就通过

RegisteredWaitHandle来动态的控制,比如说停止计数器的运行。


class Program
 {
 static void Main(string[] args)
 {
 RegisteredWaitHandle handle = null;

 AutoResetEvent ar = new AutoResetEvent(false);

 handle = ThreadPool.RegisterWaitForSingleObject(ar, Run1, null, 2000, false);

 //10s后停止
 Thread.Sleep(10000);

 handle.Unregister(ar);

 Console.WriteLine("小子,主线程要干掉你了。");

 Console.Read();
 }

 static void Run1(object obj, bool sign)
 {
 Console.WriteLine("当前时间:{0} 我是线程{1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
 }
 }


相关文章
|
1月前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
109 38
|
17天前
|
Java
.如何根据 CPU 核心数设计线程池线程数量
IO 密集型:核心数*2 计算密集型: 核心数+1 为什么加 1?即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费。
19 4
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
51 1
C++ 多线程之初识多线程
|
1月前
|
Java
线程池内部机制:线程的保活与回收策略
【10月更文挑战第24天】 线程池是现代并发编程中管理线程资源的一种高效机制。它不仅能够复用线程,减少创建和销毁线程的开销,还能有效控制并发线程的数量,提高系统资源的利用率。本文将深入探讨线程池中线程的保活和回收机制,帮助你更好地理解和使用线程池。
67 2
|
1月前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
77 4
|
1月前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
187 2
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
23 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
20 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
34 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
38 1