多线程学习笔记第三篇
多线程学习笔记第3篇
前言:这篇博客主要是在上一篇博客的基础上,继续记录了死锁,线程同步和线程池的一些知识,方便我以后继续的学习,在这里贴出来和大家共享一下
1. 对象池技术模拟
(1) 如果一些对象创建非常消耗时间和资源,使用非常频繁,这时候可以考虑使用对象池技术
(2)对象池其实就是一个数组,当我们想用一个对象的时候直接从池子里面取,别的对象也可以从池子里面取
(3)使用代码模拟对象池Demo添加锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | namespace ObjectPoolPractice { class Program { static void Main( string [] args) { //定义一个对象池 MyConnection[] pool = new MyConnection[100]; int index = 0; //当前对象池中对象的个数 //定义一个锁对象 Object objLock = new object (); //可以锁定字符串,但是尽量不要锁定字符串,因为相同的字符串指向的引用相同 //当然也可以锁定this //模拟对象池的使用,生产者和消费者的模拟 //模拟5个生产者,不停地往对象池里面添加对象 for ( int i = 0; i < 5; i++) { Thread thread = new Thread(() => { while ( true ) { //锁的对象必须是引用类型,因为同步索引块在堆上的 //只有Lock同一个对象才能互斥 lock (objLock) //阻塞当前线程,等待拿到当前对象的锁 { if (index < pool.Length) { Console.WriteLine( "生产一个对象" ); //生产对象,并将对象放到池子里面去,生产者 MyConnection conn = new MyConnection(); //放到池子里面 pool[index] = conn; index++; } //执行到最后的时候释放锁 Thread.Sleep(300); } } }); thread.IsBackground = true ; thread.Start(); } //模拟10个消费者 for ( int i = 0; i < 10; i++) { Thread thread = new Thread(() => { while ( true ) { lock (objLock) { if (index > 0) { Console.WriteLine( "消费一个对象:" + pool[index - 1].ToString()); pool[index - 1] = null ; index--; } Thread.Sleep(30); } } }); thread.IsBackground = true ; thread.Start(); } Console.ReadLine(); } } //假设创建此对象非常消耗时间 public class MyConnection { } } |
(4)数据库模仿锁
1)select * from bank with(nolock) --表示当前表做查询的时候不加锁
(5)解决死锁的方法是操作资源的顺序一致
2. 线程同步
(1) 参考上面的案例代码进行说明
(2)new在堆上申请内存空间后干了什么
1)Object objLock = new object();
2)开辟内存
3)调用构造函数
4)同步块索引(复数)
(3)Lock(语法糖)
try
{
System.Threading.Monitor.Enter(obj);
}
finally
{
System.Threading.Monitor.Exit(obj);
}
(4)lock这段代码是怎么运行的,lock语句根本使用的就是Monitor.Enter和Monitor.Exit,也就是说lock(o)时执行Monitor.Enter(this), 大括号结束时执行Monitor.Exit(this).他的意义在于什么呢,对于任何一个对象来说,他在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,他指向CLR中的SyncBlock Cache区域中的一个SyncBlock.什么意思呢?就是说,当你执行Monitor.Enter(Object)时,如果object的索引值为负数,就从SyncBlock Cache中选区一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object为正数的索引,然后就等待。直到索引变为负数,即线程使用Monitor.Exit(object)将索引变为负数。
(5)线程同步解决的问题:解决多个线程同时访问同一个资源的时候,进行同步。同一个时间,我们的资源只允许一个线程来访问。
3. 线程池
(1) 在程序中,如果某个创建某种对象所需要的代价太高,同时这个对象又可以反复使用,那么我们往往就会准备一个容器,用来保存一批这样的对象。于是乎,我们想要用这种对象时,就不需要每次去创建一个,而直接从容器中取出一个现成的对象就可以了。由于节省了创建对象的开销,程序性能自然就上升了
(2)ThreadPool class提供了一个线程池,该线程池可用于发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。 线程池允许在后台运行多个工作,而不需要为每个任务频繁地创建和销毁单独的线程,从而减少了开销。
(3)普通的Windows应用程序(如控制台或WinForm/WPF),会将其设置为"处理器数 * 250"。也就是说,如果您的机器为2个2核CPU,那么CLR线程池的容量默认上限便是1000,也就是说,它最多可以管理1000个线程同时运行
(4)WaitCallback是一个委托类型,查看源码是:
public delegate void WaitCallback(object state);
(5)线程池Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | static void Main( string [] args) { Console.WriteLine( "主线程是:" + Thread.CurrentThread.ManagedThreadId); //直接将一个方法教给线程池,在线程池内部的线程空闲的时候自动执行当前传进去的委托方法 for ( int i = 0; i < 1000; i++) { //一般情况下做异步的都要使用线程池,非常特殊的情况,比如你必须拿到当前线程的实例的时候,那么你可以考虑手动创建线程 //.net很多方面都是用线程池,,比如异步委托,只要是系统内部帮我们分配的线程都是通过线程池里面提供的线程 ThreadPool.QueueUserWorkItem( new WaitCallback(DemoWork), "韩迎龙" ); } Console.ReadLine(); } private static void DemoWork( object state) { //这是由线程池来执行的方法 Console.WriteLine( "当前执行的方法的线程是:{0}+{1}" , Thread.CurrentThread.ManagedThreadId, state); } //可以执行查看一下,这里每个人的机器结果可能都不一样,所以试一下 |
(6)线程池支持的最大线程数1023个,默认设置是1000,最小0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static void Main( string [] args) { int maxThread = 0; int currentMaxTrhead = 0; ThreadPool.GetMaxThreads( out maxThread, out currentMaxTrhead); ; Console.WriteLine( "最多线程数是:{0},当前的设置是{1}" , maxThread, currentMaxTrhead); //看最小的和最大的一摸一样 Console.ReadKey(); } //执行结果是:最多线程数是:1023,当前的设置是1000 |
(7)等待线程执行结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | static void Main( string [] args) { int a = 0; Thread thread = new Thread(() => { Thread.Sleep(3000); for ( int i = 0; i < 10; i++) { a++; } Console.WriteLine( "韩迎龙" ); }); thread.IsBackground = true ; thread.Start(); Thread.Sleep(1000); thread.Abort(); //告诉操作系统,将这个线程关闭 thread.Join(); //让当前线程阻塞,停下来等待thread执行完成 Console.WriteLine(a); Console.ReadLine(); } |
(8)工作项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static void Main( string [] args) { //工作项:比传统的线程池多了一个返回值 Task< int > task = new Task< int >(a => { return ( int )a; }, 5); task.Start(); //调用工作项执行 Console.WriteLine(task.Result); //task最好的东西就是能够获取到返回值 } |
初心商城:初心商城
作者:韩迎龙(Kencery) MVC/.NET群:159227188如果您认为这篇文章还不错或者有所收获,您可以通过右边的“打赏”功能 打赏一杯咖啡,本页版权归作者和博客园所有,欢迎转载,但未经作者同意必须保留此段声明, 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构