多线程编程学习笔记——线程池(一)
创建多线程操作是非常昂贵的,所以每个运行时间非常短的操作,创建多线程进行操作,可能并不能提高效率,反而降低了效率。
如果你有非常多的执行时间非常短的操作,那么适合作用线程池来提高效率,而不是自行创建多线程。
线程池,就是我们先分配一些资源到池子里,当我们需要使用时,则从池子中获取,用完了,再放回池子里。
.NET中的线程池是受CLR管理的,TheadTool类有一个QueueUserWorkItem静态方法,这个静态方法接受一个委托,代表用户自定义的一个异步操作,在这个方法被调用之后,委托会进入到内部队列中,如果池中没有线程,则创建一个工作线程,把第一个委托放入工作线程。如果继续放入委托,则池创建新的工作线程,直到工作线程数量达到上限。这时再放入委托,则不会创建新的工作线程,而是在队列中等待,直到有空闲的工作线程。
当线程池中所有操作都完成,而且没有新任务操作时,线程池会释放长时间不用的资源。
注意:放入线程池中的操作需要的时间要短,不要把需要长时间运行的操作放入线程池中,或阻塞工作线程。这将导致性能问题和非常难以调用的问题。
在ASP.NET中使用线程池要当心,ASP.NET中的线程池是一个共用线程池,如果线程池中的工作线程都用完了,则会造成WEB服务器对正常的HTTP请求无法提供服务。
一、 线程池中调用委托
1.代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ThreadPoolDemo { public delegate string ThreadPoolRun(out int threadId); class Program { static void Main(string[] args) { Console.WriteLine("开始测试线程池-委托。。。"); int threadId = 0; ThreadPoolRun poolDele = RunThread; var t = new Thread(() => RunThread(out threadId)); t.Start(); t.Join(); Console.WriteLine("线程ID {0} ",threadId); IAsyncResult r = poolDele.BeginInvoke(out threadId, Callback, "在线程池中同步调用回调函数"); string result = poolDele.EndInvoke(out threadId, r); Console.WriteLine("线程池中工作线程ID :{0}", threadId); Console.WriteLine("返回结果:{0}",result); Thread.Sleep(2000); Console.Read(); } private static void Callback(IAsyncResult r) { Console.WriteLine("开始调用回调函数。。。"); Console.WriteLine("回调函数此时的状态 :{0}",r.AsyncState); Console.WriteLine("调用此回调函数的线程是否在线程池 :{0}", Thread.CurrentThread.IsThreadPoolThread); Console.WriteLine("调用此回调函数的线程在线程池在的ID :{0}", Thread.CurrentThread.ManagedThreadId); } private static string RunThread(out int threadId) { Console.WriteLine("开始工作。。。"); Console.WriteLine("调用此回调函数的线程是否在线程池 :{0}", Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(2)); threadId = Thread.CurrentThread.ManagedThreadId; return string.Format("此线程在线程池在的ID :{0}", threadId); } } }
2.程序执行结果如下图。
上面的程序运行时,我们首先创建线程来执行委托操作,然后调用委托的BeginInvoke来执行回调方法,这个回调函数会在异步操作完成之后会被调用,并且会把一个自定义的值传给这个回调函数,最后我们会得到一个实现了IAsyncResult接口的result对象,当线程池的工作线程在进行工作时,允许我们继续其他操作。我们可以轮询result对象的IsCompleted属性,确定操作是否完成。也可以调用EndInvoke将IAsyncResult传给委托参数。
二、 线程池中放入异步操作
1.代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ThreadPoolDemo { class Program { static void Main(string[] args) { Console.WriteLine("开始测试线程池-QueueUserWorkItem。。。"); const int x = 1; const int y = 2; string workState = "工作状态 2"; ThreadPool.QueueUserWorkItem(AsyncOper); Thread.Sleep(1000); ThreadPool.QueueUserWorkItem(AsyncOper,"同步状态"); Thread.Sleep(1000); ThreadPool.QueueUserWorkItem(status => { Console.WriteLine("操作状态 {0} ", status); Console.WriteLine("线程池中工作线程ID :{0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(TimeSpan.FromSeconds(2)); },"工作状态"); ThreadPool.QueueUserWorkItem(_ => { Console.WriteLine("操作结果x+y= {0} ,{1}", x+y,workState); Console.WriteLine("线程池中工作线程ID :{0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(TimeSpan.FromSeconds(2)); }, "工作状态"); Thread.Sleep(2000); Console.Read(); } private static void AsyncOper(object status) { Console.WriteLine("操作状态 :{0} ",status??"null"); Console.WriteLine("工作线程在线程池在的ID :{0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); } } }
2.执行结果如下。程序执行了两次。
程序首先定义了一个AsyncOper方法,然后使用QueueUserWorkItem将这个方法放入线程池中,然后再次放入一个AsyncOper方法,不过这次给方法调用传一个对象。
代码中的调用Thread.sleep方法,是为了让线程池中的工作线程为新操作重用。请注意打印出来的ThradId,如果ThreadID一样则证明两个操作重用了同一个工作线程。
相关文章
- 学习ASP.NET Core Razor 编程系列七——修改列表页面
- 学习ASP.NET Core Razor 编程系列五——Asp.Net Core Razor新建模板页面
- 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
- 学习ASP.NET Core Razor 编程系列三——创建数据表及创建项目基本页面
- 初探语音识别ASR算法
- 撬动百亿VRAR产业,让VR们“造”起来
- 今天谈谈用户故事地图,不是用户故事
- 带你掌握二进制SCA检测工具的短板及应对措施
- 云图说|ModelArts Pro:让AI开发更简单
- HDZ城市行深圳站|AIoT时代,如何抓住智联生活的战略机会点?
- 1个通用工具平台+多个热点场景工具套件,助力开发者快速构建应用
- 左手自研,右手开源,技术揭秘华为云如何领跑容器市场
- 云小课|DGC数据开发之基础入门篇
- 图解带你掌握`JVM`运行时核心内存区
- TCP 两次握手为什么无法阻止历史连接?
- AI新手语音入门:认识词错率WER与字错率CER
- 华为超大云数据中心落地贵州,这些硬核技术有利支撑“东数西算”
- 在高并发环境下该如何构建应用级缓存
- 云小课|云小课带你快速掌握云数据迁移CDM
- 云图说|初识ModelArts开发者生态社区——AI Gallery