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);
 }
 }


相关文章
|
9月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
391 0
|
6月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
407 2
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
649 60
【Java并发】【线程池】带你从0-1入门线程池
|
9月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
10月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
708 5
|
监控 Kubernetes Java
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
|
12月前
|
Java
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
315 18
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
541 20
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
878 64
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。

热门文章

最新文章