【.NET Core】多线程之线程池(ThreadPool)详解(二)

简介: 【.NET Core】多线程之线程池(ThreadPool)详解(二)

【.NET Core】多线程之线程池(ThreadPool)详解(二)

在上一篇《【.NET Core】多线程之线程池(ThreadPool)详解(一)》中我们详细讲解了,线程池概念,如何应用及其应用的场景。本文我们将着重讲解线程池的使用。

一、线程池原理

CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程。线程池初始化时是没有线程的,线程池里的线程的初始化与其他线程一样,但是在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。


这样既节省建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销。


通过CLR线程池所建立的线程总是默认为后台线程,优先级数为ThreadPriotity.Normal。


CLR线程池分为工作者线程(WorkerThreads)与I/O线程(CompletionPortThreads)两种:


工作者线程是主要用作管理CLR内部对象的运作,通常用于计算密集的任务。


I/O(Input/Output)线程主要用于与外部对象的运作,通常用于计算密集的任务。


二、通过ThreadPool.QueueUserWorkItem()方法创建线程池

const int cycleNum = 15;
static void Main(string[] args)
{
    // 设置CLR线程池中工作者线程与I/O线程最大数目和最小数目
    ThreadPool.SetMinThreads(1, 1);
    ThreadPool.SetMaxThreads(10, 10);
    for (int i = 1; i <= cycleNum; i++)
    {
       // 将方法派入队列,成功返回TURE否则异常System.ArgumentNullException
       ThreadPool.QueueUserWorkItem(new WaitCallback(testFun), i.ToString());
    }
    Console.WriteLine("主线程执行!");
    Thread.Sleep(5300);
    Console.WriteLine("主线程结束!");
    Console.ReadKey();
}
public static void testFun(object obj)
{
    Console.WriteLine(string.Format("{0}:第{1}个线 程,{2}当前线程名称", DateTime.Now.ToString(), obj.ToString(),Thread.CurrentThread.ThreadState));
    Thread.Sleep(5000);
}

从上面示例你会注意到ThreadPool线程没有Join方法。你无法通过任何直接方法确定线程是否已完成执行。一旦你在ThreadPool中排队工作项,主线程就会继续执行。如果要等到线程完成执行,则必须使用同步事件编写代码。


使用线程同步事件

线程同步事件有两种类型:

  1. ManualResetEvent这是一个像我们家中的普通门一样工作事件,你可以使用.Set()方法设置它并使用.Reset()方法重置(关闭)它。它将阻塞调用.WaitOne()的线程,直到它被设置。设置后,事件对象的状态将处于Set状态,直到你使用.Reset()方法手动重置它。
  2. AutoResetEvent-它的作用与ManualResetEvent相同,只是它的作用类似自动门。一旦你设置它,它运行通过调用.WaitOne()等待的线程通过,然后将自身重置回来。
static void Main(string[] args)
{
     ManualResetEvent myWaitHandle = new ManualResetEvent(false);
     ThreadPool.QueueUserWorkItem(new WaitCallback(RunThread), myWaitHandle);
     myWaitHandle.WaitOne();
     Console.WriteLine("ThreadPool thread has completed the Work and Set myWaitHandle");
     Console.ReadLine();
}

private static void RunThread(object state)
{
     ManualResetEvent waitHandleFromParent = (ManualResetEvent)state;
     Console.WriteLine("I am in ThreadPool Thread");
     Thread.Sleep(5000);
     Console.WriteLine("ThreadPool thread is going to exit");
     waitHandleFromParent.Set();
}

二、通过Task创建线程池

2010年引入的任务并行库中的任务类为你提供了上述两个问题的解决方法。许多人将任务与轻量级线程混淆,但任务不能与线程相提并论。任务只是一组要执行的作业。线程执行调度到TaskScheduler的任务。任务不保证并行处理,并根据资源的可用性进行调度。默认情况为任务衍生新线程。与Thread与ThreadPool使用Task可以返回执行结果。

任务不是异步运行的,因为我们在主线程中调用task.Wait(),主线程被阻塞直到任务完成。任务非常适合使用async/await进行异步执行。下面将演示Task创建线程过程:

public static void Main(string[] args)
{
     var task = new Task(RunTask);
     task.Start();
     task.Wait();
     Console.WriteLine("Back to main thread. Task completed execution!");
     Console.ReadLine();
}
 
private static void RunTask()
{
     Console.WriteLine("I am in Task");
     Thread.Sleep(5000);
}


三、IasyncResult异步线程池

.NET Framework允许你异步调用任何方法。定义与你需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的BeginInvoke和EndInvoke方法。


BeginInvoke方法用于启动异步调用。它与你需要异步执行的方法具有相同的参数,只不过还有两个额外的参数。BeginInvoke立即返回,不等待异步调用完成。BeginInvoke返回IasyncResult,可用于监视调用进度。


EndInvoke方法用于检索异步调用结果。调用BeginInvoke后可随时调用EndInvoke方法;如果异步调用未完成,EndInvoke将一直阻塞到异步调用完成。EndInvoke的参数包括你需要异步执行的方法的out和ref参数以及由BeginInvoke返回的IAsyncResult。

//声明委托
public delegate void AsyncEventHandler();
//异步方法
void Event1()
{
  Console.WriteLine("Event1 Start");
  System.Threading.Thread.Sleep(4000);
  Console.WriteLine("Event1 End");
}

// 同步方法
void Event2()
{
  Console.WriteLine("Event2 Start");
  int i=1;
  while(i<1000)
  {
    i=i+1;
    Console.WriteLine("Event2 "+i.ToString());
  }
    Console.WriteLine("Event2 End");
}


static void Main(string[] args)
{
   long start=0;
   long end=0;
   Class1 c = new Class1();
   Console.WriteLine("ready");
   start=DateTime.Now.Ticks;
   //实例委托
   AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
   //异步调用开始,没有回调函数和AsyncState,都为null
   IAsyncResult ia = asy.BeginInvoke(null, null);
   //同步开始,
   c.Event2();
    //异步结束,若没有结束,一直阻塞到调用完成,在此返回该函数的return,若有返回值。
   asy.EndInvoke(ia);
    //都同步的情况。
     end =DateTime.Now.Ticks;
   Console.WriteLine("时间刻度差="+ Convert.ToString(end-start) );
   Console.ReadLine();
}

四、总结

上面说过,.net framework 可以异步调用任何方法。所以异步用处广泛。


在.net framework 类库中也有很多异步调用的方法。一般都是已Begin开头End结尾构成一对,异步委托方法,外加两个回调函数和AsyncState参数,组成异步操作的宏观体现。所以要做异步编程,不要忘了委托delegate、Begin,End,AsyncCallBack委托,AsyncState实例(在回调函数中通过IAsyncResult.AsyncState来强制转换),IAsycResult(监控异步),就足以理解异步真谛了。

目录
打赏
0
3
3
1
41
分享
相关文章
|
5月前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
305 60
【Java并发】【线程池】带你从0-1入门线程池
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
151 5
|
3月前
|
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
92 18
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
135 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
ASP.NET Core 中的速率限制中间件
在ASP.NET Core中,速率限制中间件用于控制客户端请求速率,防止服务器过载并提高安全性。通过`AddRateLimiter`注册服务,并配置不同策略如固定窗口、滑动窗口、令牌桶和并发限制。这些策略可在全局、控制器或动作级别应用,支持自定义响应处理。使用中间件`UseRateLimiter`启用限流功能,并可通过属性禁用特定控制器或动作的限流。这有助于有效保护API免受滥用和过载。 欢迎关注我的公众号:Net分享 (239字符)
141 1
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
170 3
|
8月前
|
.如何根据 CPU 核心数设计线程池线程数量
IO 密集型:核心数*2 计算密集型: 核心数+1 为什么加 1?即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费。
304 4
.net core 非阻塞的异步编程 及 线程调度过程
【11月更文挑战第12天】本文介绍了.NET Core中的非阻塞异步编程,包括其基本概念、实现方式及应用示例。通过`async`和`await`关键字,程序可在等待I/O操作时保持线程不被阻塞,提高性能。文章还详细说明了异步方法的基础示例、线程调度过程、延续任务机制、同步上下文的作用以及如何使用`Task.WhenAll`和`Task.WhenAny`处理多个异步任务的并发执行。
144 1
登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问