【合】C#线程
浅谈ThreadPool 线程池
地址:https://www.cnblogs.com/xugang/archive/2010/04/20/1716042.html
相关概念:
线程池可以看做容纳线程的容器;
一个应用程序最多只能有一个线程池;
ThreadPool静态类通过QueueUserWorkItem()方法将工作函数排入线程池;
每排入一个工作函数,就相当于请求创建一个线程;
线程池的作用:
线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。
什么时候使用ThreadPool?
ThreadPool 示例一 :
ThreadPool_1.cs using System; using System.Text; using System.Threading; namespace 多线程 { public class Example { public static void Main() { // Queue the task. ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc)); Console.WriteLine("Main thread does some work, then sleeps."); Thread.Sleep(1000); Console.WriteLine("Main thread exits."); } static void ThreadProc(Object stateInfo) { // No state object was passed to QueueUserWorkItem, // so stateInfo is null. Console.WriteLine("Hello from the thread pool."); } } }
ThreadPool 示例二 :
ThreadPool_2.cs using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace CS_Test { class ThreadPool_Demo { // 用于保存每个线程的计算结果 static int[] result = new int[10]; //注意:由于WaitCallback委托的声明带有参数, // 所以将被调用的Fun方法必须带有参数,即:Fun(object obj)。 static void Fun(object obj) { int n = (int)obj; //计算阶乘 int fac = 1; for (int i = 1; i <= n; i++) { fac *= i; } //保存结果 result[n] = fac; } static void Main(string[] args) { //向线程池中排入9个工作线程 for (int i = 1; i <= 9 ; i++) { //QueueUserWorkItem()方法:将工作任务排入线程池。 ThreadPool.QueueUserWorkItem(new WaitCallback(Fun),i); // Fun 表示要执行的方法(与WaitCallback委托的声明必须一致)。 // i 为传递给Fun方法的参数(obj将接受)。 } //输出计算结果 for (int i = 1; i <= 9; i++) { Console.WriteLine("线程{0}: {0}! = {1}",i,result[i]); } } } }
ThreadPool的作用:
多线程实现Thread.Start()与ThreadPool.QueueUserWorkItem两种方式对比
地址:https://www.cnblogs.com/ChineseMoonGod/p/5341253.html
Thread.Start(),ThreadPool.QueueUserWorkItem都是在实现多线程并行编程时常用的方法。两种方式有何异同点,而又该如何取舍?
写一个Demo,分别用两种方式实现。观察各自的现象。
一个WorkMan class,其内的method doSomething()是每次异步线程调用的方法。该方法只是随机的让线程休眠一段时间。

public void doSomething() { OnBegin(new EventArgs()); // someone does something here var r = new Random(); int sleepTime = r.Next(3000, 180000); Thread.Sleep(900000); OnCompleted(new EventArgs()); } doSomething

Thread.Start()方式实现

workThreads = new Thread[NUMBER_OF_THREADS]; for (var i = 0; i < NUMBER_OF_THREADS; i++) { arrWorkMen[i] = new WorkMan() { WorkStarted = true, InstanceID = startThreadNumber }; arrWorkMen[i].BeginHandler += HandleTaskBegin; arrWorkMen[i].CompletedHandler += HandleTaskCompleted; // create a thread and attach to the object var st = new ThreadStart(arrWorkMen[i].doSomething); workThreads[i] = new Thread(st); startThreadNumber++; } for (var i = 0; i < NUMBER_OF_THREADS; i++) { Thread.Sleep(2000); workThreads[i].Start(); } Thread.Start()

ThreadPool.QueueUserWorkItem方式实现

for (var i = 0; i < NUMBER_OF_THREADS; i++) { arrWorkMen[i] = new WorkMan() { WorkStarted = true, InstanceID = startThreadNumber }; arrWorkMen[i].BeginHandler += HandleTaskBegin; arrWorkMen[i].CompletedHandler += HandleTaskCompleted; startThreadNumber++; } for (var i = 0; i < NUMBER_OF_THREADS; i++) { Thread.Sleep(2000); ThreadPool.QueueUserWorkItem(o => arrWorkMen[i].doSomething()); } ThreadPool.QueueUserWorkItem

观察两种方式下,线程创建和回收的情况。
同样的场景,每2秒钟发起一新的线程,且每一线程均休眠2分钟。Thread.Start()实现下,线程一路最高飙升到71个,然后随着2分钟后休眠线程的结束,线程个数始终在70、71之间徘徊。而ThreadPool.QueueUserWorkItem的实现下,线程个数到达最高73后,始终在72、73之间徘徊。
总体来说,做同样的事情。ThreadPool方式产生的线程数略高于Thread.Start()。Thread.Start()产生的线程在完成任务后,很快被系统所回收。而ThreadPool(线程池)方式下,线程在完成工作后会被保留一段时间以备resue。所以,当需求需要大量线程并发工作的时候,不建议使用ThreadPool方式,因为它会保持很多额外的线程。
此处摘录一段来自网络的参考:
As for the ThreadPool
, it is designed to use as few threads as possible while also keeping the CPU busy. Ideally, the number of busy threads is equal to the number of CPU cores. However, if the pool detects that its threads are currently not using the CPU (sleeping, or waiting for another thread), it starts up more threads (at a rate of 1/second, up to some maximum) to keep the CPU busy.
难道调用ThreadPool.QueueUserWorkItem()的时候,真是必须调用Thread.Sleep(N)吗?
地址:https://www.cnblogs.com/artech/archive/2009/05/22/1486761.html
开门见山,下面的例子中通过调用ThreadPool.QueueUserWorkItem(WaitCallback callBack, object state)的方式实现异步调用:
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: List<Action> actions = new List<Action>();
6: actions.Add(() => Console.WriteLine("A1"));
7: actions.Add(() => Console.WriteLine("A2"));
8: actions.Add(() => Console.WriteLine("A3"));
9: actions.Add(() => Console.WriteLine("A4"));
10:
11: foreach (var action in actions)
12: {
13: ThreadPool.QueueUserWorkItem(state => action(), null);
14: }
15:
16: Console.Read();
17: }
18: }
但是出现错误的输出结果:
解决的方案就是在每次For循环中,调用Thread.Sleep休眠当前线程,哪怕是1ms:
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: List<Action> actions = new List<Action>();
6: actions.Add(() => Console.WriteLine("A1"));
7: actions.Add(() => Console.WriteLine("A2"));
8: actions.Add(() => Console.WriteLine("A3"));
9: actions.Add(() => Console.WriteLine("A4"));
10:
11: foreach (var action in actions)
12: {
13: ThreadPool.QueueUserWorkItem(state => action(), null);
14:
15: Thread.Sleep(1);
16: }
17:
18: Console.Read();
19: }
20: }
21:
这次能够输出正确的结果:
我们也看到很多人确实是这么做的。但是如果真是必须这样的话,这样的编程方式很难让我接受,不知道大家有何高见。
在老赵的提示下,醒悟过来:由于被置于ThreadPool中的操作时异步的,还没有来的执行的时候,action已经被for循环改变,永远是同一个action对象! 呵呵,脑袋有时候有点转不过弯!
所以正确的写法是:
1: foreach (var action in actions)
2: {
3: var a = action;
4: ThreadPool.QueueUserWorkItem(state => a(), null);
5: }
【合】C#线程的更多相关文章
- java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)
一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...
- RxJava 教程-1 简介 原理 线程控制 变换
简介 RxJava 是什么? RxJava 在 GitHub 主页上的自我介绍是 RxJava is a Java VM implementation of ReactiveX: a library ...
- Thread(线程)四
今天继续讲讲线程的异常处理.线程取消.多线程的临时变量和线程安全lock的问题. 1.异步处理. 一般来说如果是同步方法的异步处理,我们大多都是try catch住,但是异步方法应该怎么做呢. #re ...
- Linux 循环创建多个线程
这里说一下相关的基础知识: 线程概念 什么是线程 LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下) 进程:独立地址空间,拥有PCB 线 ...
- 企业级任务调度框架Quartz(7) 线程在Quartz里的意义(1)
1.Java 中的线程 线程允许程序同一时间做很多任务,至少,看起来那些任务是并发执行的.在我的并发编程的帖子里有介绍线程的基本概念:我们知道在任一特定时刻仅有一个线程 在执行,但是 CPU ...
- linux系统编程--线程
安装线程man page,命令:sudo apt-get install manpages-posix-dev 线程概念 什么是线程 LWP:light weight process 轻量级的进程,本 ...
- C#夯实基础之多线程一:初识多线程
一. 烧水沏茶问题 在小学四年级有一个烧水沏茶问题,可以作为我们今天讨论话题的引子: 客人来了,要烧一壶茶,但是烧水需要5分钟,洗水壶需要1分钟,洗茶杯需要2分钟,接水需要1分钟,找茶叶需 ...
- 78. Android之 RxJava 详解
转载:http://gank.io/post/560e15be2dca930e00da1083 前言 我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Fli ...
- 给 Android 开发者的 RxJava 详解
我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 .而最近这几个 ...
- 一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库 RxJava,相当好
https://github.com/ReactiveX/RxJava https://github.com/ReactiveX/RxAndroid RX (Reactive Extensions,响 ...
随机推荐
- yxy-插入formid-并发/压力(入参的变量化)
1.抓取接口如下 2.该接口为插入数据库操作,由于前段操作频繁,就简单压测一下 接口中formId.accountId.openId为变量 formId为随机生成插入:accountId.openId ...
- python中封装
封装 引子 从封装的本身意思去理解,封装就是用一个袋子,把买的水果.书.水杯一起装进袋子里,然后再把袋子的口给封上,照这样的理解来说,封装=隐藏,但是,这种理解是片面的 ## 如何封装 在python ...
- ubuntu 终端命令颜色的修改
http://blog.chinaunix.net/uid-13954789-id-3137184.html http://blog.chinaunix.net/uid-26021340-id-348 ...
- Java之OO的特性与原则
OO(Object-Oriented)面向对象 面向对象,有三个特性.五个原则之说. 特性 封装 顾名思义,封装就好比一个黑盒子,内部被隐藏,只有几个接口与外界相连.具体来说,所谓封装 ...
- Jmeter查看结果树
取样结果: Thread Name: test 1-2 线程名称:测试1 - 2Sample S ...
- 51nod 1600 Simple KMP【后缀自动机+LCT】【思维好题】*
Description 对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i) 显然对于一个字符串,如果我们将每个0<=i&l ...
- WPF 绘制对齐像素的清晰显示的线条
此前有小伙伴询问我为何他 1 像素的线条显示发虚,然后我告诉他是“像素对齐”的问题,然而他设置了各种对齐像素的属性依旧没有作用.于是我对此进行了一系列试验,对 WPF 像素对齐的各种方法进行了一次总结 ...
- 为iPhone 6设计自适应布局(iOS8)
Apple从iOS 6加入了Auto Layout后开始就比较委婉的开始鼓励.建议开发者使用自适应布局,但是到目前为止,我感觉大多数开发者一直在回避这个问题,不管是不是由于历史原因造成的,至少他们在心 ...
- SpringBoot 接收 单个String入参之解决方案
场景: 在做接口时,有的时候,接口入参只需要一个参数,如果将一个参数封装成一个对象很麻烦,故有了以下方式: 思路: spring自带的参数解析器貌似是不具备这个能力的,所有自定义 方式方法: 1.定义 ...
- div,css常用技术
1,<div></div>一张图作为背景的用法: 必须指定width,height,background属性 .smallCircle{ margin-top: 25px; ...