ThreadPool类(线程池)
CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程。
线程池初始化时是没有线程的,线程池里的线程的初始化与其他线程一样,但是在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。
这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销。
通过CLR线程池所建立的线程总是默认为后台线程,优先级数为ThreadPriority.Normal。
CLR线程池分为工作者线程(workerThreads)与I/O线程(completionPortThreads)两种:
- 工作者线程是主要用作管理CLR内部对象的运作,通常用于计算密集的任务。
- I/O(Input/Output)线程主要用于与外部系统交互信息,如输入输出,CPU仅需在任务开始的时候,将任务的参数传递给设备,然后启动硬件设备即可。等任务完成的时候,CPU收到一个通知,一般来说是一个硬件的中断信号,此时CPU继续后继的处理工作。在处理过程中,CPU是不必完全参与处理过程的,如果正在运行的线程不交出CPU的控制权,那么线程也只能处于等待状态,即使操作系统将当前的CPU调度给其他线程,此时线程所占用的空间还是被占用,而并没有CPU处理这个线程,可能出现线程资源浪费的问题。如果这是一个网络服务程序,每一个网络连接都使用一个线程管理,可能出现大量线程都在等待网络通信,随着网络连接的不断增加,处于等待状态的线程将会很消耗尽所有的内存资源。可以考虑使用线程池解决这个问题。
线程池的最大值一般默认为1000、2000。当大于此数目的请求时,将保持排队状态,直到线程池里有线程可用。
使用CLR线程池的工作者线程一般有两种方式:
- 通过ThreadPool.QueueUserWorkItem()方法;
- 通过委托;
要注意,不论是通过ThreadPool.QueueUserWorkItem()还是委托,调用的都是线程池里的线程。
通过以下两个方法可以读取和设置CLR线程池中工作者线程与I/O线程的最大线程数。
- ThreadPool.GetMax(out in workerThreads,out int completionPortThreads);
- ThreadPool.SetMax(int workerThreads,int completionPortThreads);
若想测试线程池中有多少线程正在投入使用,可以通过ThreadPool.GetAvailableThreads(out in workThreads,out int conoletionPortThreads)方法。
方法 | 说明 |
GetAvailableThreads | 剩余空闲线程数 |
GetMaxThreads | 最多可用线程数,所有大于此数目的请求将保持排队状态,直到线程池线程变为可用 |
GetMinThreads | 检索线程池在新请求预测中维护的空闲线程数 |
QueueUserWorkItem | 启动线程池里得一个线程(队列的方式,如线程池暂时没空闲线程,则进入队列排队) |
SetMaxThreads | 设置线程池中的最大线程数 |
SetMinThreads | 设置线程池最少需要保留的线程数 |
我们可以使用线程池来解决上面的大部分问题,跟使用单个线程相比,使用线程池有如下优点:
1、缩短应用程序的响应时间。因为在线程池中有线程的线程处于等待分配任务状态(只要没有超过线程池的最大上限),无需创建线程。
2、不必管理和维护生存周期短暂的线程,不用在创建时为其分配资源,在其执行完任务之后释放资源。
3、线程池会根据当前系统特点对池内的线程进行优化处理。
总之使用线程池的作用就是减少创建和销毁线程的系统开销。在.NET中有一个线程的类ThreadPool,它提供了线程池的管理。
ThreadPool是一个静态类,它没有构造函数,对外提供的函数也全部是静态的。其中有一个QueueUserWorkItem方法,它有两种重载形式,如下:
public static bool QueueUserWorkItem(WaitCallback callBack):将方法排入队列以便执行。此方法在有线程池线程变得可用时执行。
public static bool QueueUserWorkItem(WaitCallback callBack,Object state):将方法排入队列以便执行,并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。
QueueUserWorkItem方法中使用的的WaitCallback参数表示一个delegate,它的声明如下:
public delegate void WaitCallback(Object state)
如果需要传递任务信息可以利用WaitCallback中的state参数,类似于ParameterizedThreadStart委托。
下面是一个ThreadPool的例子,代码如下:
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading; namespace ConsoleApp1
{
class ThreadPoolDemo
{
public ThreadPoolDemo()
{
} public void Work()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(CountProcess));
ThreadPool.QueueUserWorkItem(new WaitCallback(GetEnvironmentVariables));
}
/// <summary>
/// 统计当前正在运行的系统进程信息
/// </summary>
/// <param name="state"></param>
private void CountProcess(object state)
{
Process[] processes = Process.GetProcesses();
foreach (Process p in processes)
{
try
{
Console.WriteLine("进程信息:Id:{0},ProcessName:{1},StartTime:{2}", p.Id, p.ProcessName, p.StartTime);
}
catch (Win32Exception e)
{
Console.WriteLine("ProcessName:{0}", p.ProcessName);
}
finally
{
}
}
Console.WriteLine("获取进程信息完毕。");
}
/// <summary>
/// 获取当前机器系统变量设置
/// </summary>
/// <param name="state"></param>
public void GetEnvironmentVariables(object state)
{
IDictionary list = System.Environment.GetEnvironmentVariables();
foreach (DictionaryEntry item in list)
{
Console.WriteLine("系统变量信息:key={0},value={1}", item.Key, item.Value);
}
Console.WriteLine("获取系统变量信息完毕。");
}
}
}
ThreadPoolDemo
using System;
using System.Threading; namespace ConsoleApp1
{ class Program
{
static void Main(string[] args)
{
ThreadPoolDemo tpd1 = new ThreadPoolDemo();
tpd1.Work();
Thread.Sleep();
Console.WriteLine("OK");
Console.ReadLine();
}
}
}
Program
利用ThreadPool调用工作线程和IO线程的范例
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Threading; namespace ConsoleApp1
{ class Program
{
static void Main(string[] args)
{
// 设置线程池中处于活动的线程的最大数目
// 设置线程池中工作者线程数量为1000,I/O线程数量为1000
ThreadPool.SetMaxThreads(, );
Console.WriteLine("Main Thread: queue an asynchronous method");
PrintMessage("Main Thread Start"); // 把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法
ThreadPool.QueueUserWorkItem(asyncMethod);
asyncWriteFile();
Console.Read();
} // 方法必须匹配WaitCallback委托
private static void asyncMethod(object state)
{
Thread.Sleep();
PrintMessage("Asynchoronous Method");
Console.WriteLine("Asynchoronous thread has worked ");
} #region 异步读取文件模块
private static void asyncReadFile()
{
byte[] byteData = new byte[];
FileStream stream = new FileStream(@"D:\123.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, , true);
//把FileStream对象,byte[]对象,长度等有关数据绑定到FileDate对象中,以附带属性方式送到回调函数
Hashtable ht = new Hashtable();
ht.Add("Length", (int)stream.Length);
ht.Add("Stream", stream);
ht.Add("ByteData", byteData); //启动异步读取,倒数第二个参数是指定回调函数,倒数第一个参数是传入回调函数中的参数
stream.BeginRead(byteData, , (int)ht["Length"], new AsyncCallback(Completed), ht);
PrintMessage("asyncReadFile Method");
} //实际参数就是回调函数
static void Completed(IAsyncResult result)
{
Thread.Sleep();
PrintMessage("asyncReadFile Completed Method");
//参数result实际上就是Hashtable对象,以FileStream.EndRead完成异步读取
Hashtable ht = (Hashtable)result.AsyncState;
FileStream stream = (FileStream)ht["Stream"];
int length = stream.EndRead(result);
stream.Close();
string str = Encoding.UTF8.GetString(ht["ByteData"] as byte[]);
Console.WriteLine(str);
stream.Close();
}
#endregion #region 异步写入文件模块
//异步写入模块
private static void asyncWriteFile()
{
//文件名 文件创建方式 文件权限 文件进程共享 缓冲区大小为1024 是否启动异步I/O线程为true
FileStream stream = new FileStream(@"D:\123.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, , true);
//这里要注意,如果写入的字符串很小,则.Net会使用辅助线程写,因为这样比较快
byte[] bytes = Encoding.UTF8.GetBytes("你在他乡还好吗?");
//异步写入开始,倒数第二个参数指定回调函数,最后一个参数将自身传到回调函数里,用于结束异步线程
stream.BeginWrite(bytes, , (int)bytes.Length, new AsyncCallback(Callback), stream);
PrintMessage("AsyncWriteFile Method");
} static void Callback(IAsyncResult result)
{
//显示线程池现状
Thread.Sleep();
PrintMessage("AsyncWriteFile Callback Method");
//通过result.AsyncState再强制转换为FileStream就能够获取FileStream对象,用于结束异步写入
FileStream stream = (FileStream)result.AsyncState;
stream.EndWrite(result);
stream.Flush();
stream.Close();
asyncReadFile();
}
#endregion // 打印线程池信息
private static void PrintMessage(String data)
{
int workthreadnumber;
int iothreadnumber; // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
// 获得的可用I/O线程数量给iothreadnumber变量
ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workthreadnumber.ToString(),
iothreadnumber.ToString());
}
}
}
Program
线程池中放入异步操作
using System;
using System.Threading; namespace ConsoleApp1
{ class Program
{
private static void AsyncOperation(object state)
{
Console.WriteLine("Operation state: {0}", state ?? "(null)");
Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(TimeSpan.FromSeconds());
} static void Main(string[] args)
{
const int x = ;
const int y = ;
const string lambdaState = "lambda state 2"; ThreadPool.QueueUserWorkItem(AsyncOperation);
Thread.Sleep(TimeSpan.FromSeconds()); ThreadPool.QueueUserWorkItem(AsyncOperation, "async state");
Thread.Sleep(TimeSpan.FromSeconds()); ThreadPool.QueueUserWorkItem(state => {
Console.WriteLine("Operation state: {0}", state);
Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(TimeSpan.FromSeconds());
}, "lambda state"); ThreadPool.QueueUserWorkItem(_ =>
{
Console.WriteLine("Operation state: {0}, {1}", x + y, lambdaState);
Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(TimeSpan.FromSeconds());
}, "lambda state"); Thread.Sleep(TimeSpan.FromSeconds());
}
}
}
Program
线程池同步操作
using System;
using System.Threading; namespace ConsoleApp1
{
class ThreadPoolDemo
{
static object lockobj = new object();
static int Count = ;
ManualResetEvent manualEvent;
public ThreadPoolDemo(ManualResetEvent manualEvent)
{
this.manualEvent = manualEvent;
}
public void DisplayNumber(object a)
{ lock (lockobj)
{
Count++;
Console.WriteLine("当前运算结果:{0},Count={1},当前子线程id:{2} 的状态:{3}", a, Count, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);
}
//Console.WriteLine("当前运算结果:{0}", a);
//Console.WriteLine("当前运算结果:{0},当前子线程id:{1} 的状态:{2}", a,Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);
//这里是方法执行时间的模拟,如果注释该行代码,就能看出线程池的功能了
Thread.Sleep();
//Console.WriteLine("当前运算结果:{0},Count={1},当前子线程id:{2} 的状态:{3}", a, Count, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);
//这里是释放共享锁,让其他线程进入
manualEvent.Set(); }
}
}
ThreadPoolDemo
using System;
using System.Diagnostics;
using System.Threading; namespace ConsoleApp1
{ class Program
{
//设定任务数量
static int count = ;
static void Main(string[] args)
{
//让线程池执行5个任务所以也为每个任务加上这个对象保持同步
ManualResetEvent[] events = new ManualResetEvent[count];
Console.WriteLine("当前主线程id:{0}", Thread.CurrentThread.ManagedThreadId); Stopwatch sw = new Stopwatch();
sw.Start();
NoThreadPool(count);
sw.Stop();
Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds); sw.Reset();
sw.Start();
//循环每个任务
for (int i = ; i < count; i++)
{
//实例化同步工具
events[i] = new ManualResetEvent(false);
//Test在这里就是任务类,将同步工具的引用传入能保证共享区内每次只有一个线程进入
ThreadPoolDemo tst = new ThreadPoolDemo(events[i]);
//Thread.Sleep(200);
//将任务放入线程池中,让线程池中的线程执行该任务
ThreadPool.QueueUserWorkItem(tst.DisplayNumber, i);
}
//注意这里,设定WaitAll是为了阻塞调用线程(主线程),让其余线程先执行完毕,
//其中每个任务完成后调用其set()方法(收到信号),当所有
//的任务都收到信号后,执行完毕,将控制权再次交回调用线程(这里的主线程)
ManualResetEvent.WaitAll(events);
sw.Stop();
Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);
//Console.WriteLine("所有任务做完!");
Console.ReadKey();
} static void NoThreadPool(int count)
{
for (int i = ; i < count; i++)
{
Thread.Sleep();
Console.WriteLine("当前运算结果:{0},Count={1},当前子线程id:{2} 的状态:{3}", i, i + , Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);
}
} }
}
Program
线程池中的取消操作
using System;
using System.Threading; namespace ConsoleApp1
{ class Program
{
static void Main(string[] args)
{
ThreadPool.SetMaxThreads(, );
Console.WriteLine("Main thread run");
PrintMessage("Start");
Run();
Console.ReadKey();
} private static void Run()
{
CancellationTokenSource cts = new CancellationTokenSource(); // 这里用Lambda表达式的方式和使用委托的效果一样的,只是用了Lambda后可以少定义一个方法。
// 这在这里就是让大家明白怎么lambda表达式如何由委托转变的
////ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000));
ThreadPool.QueueUserWorkItem(callback, cts.Token); Console.WriteLine("Press Enter key to cancel the operation\n");
Console.ReadLine(); // 传达取消请求
cts.Cancel();
Console.ReadLine();
} private static void callback(object state)
{
Thread.Sleep();
PrintMessage("Asynchoronous Method Start");
CancellationToken token = (CancellationToken)state;
Count(token, );
} // 执行的操作,当受到取消请求时停止数数
private static void Count(CancellationToken token, int countto)
{
for (int i = ; i < countto; i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Count is canceled");
break;
} Console.WriteLine(i);
Thread.Sleep();
} Console.WriteLine("Cout has done");
} // 打印线程池信息
private static void PrintMessage(String data)
{
int workthreadnumber;
int iothreadnumber; // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
// 获得的可用I/O线程数量给iothreadnumber变量
ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workthreadnumber.ToString(),
iothreadnumber.ToString());
}
}
}
Program
Thread与ThreadPool的一个性能比较
using System;
using System.Diagnostics;
using System.Threading; namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
const int numberOfOperations = ;
var sw = new Stopwatch();
sw.Start();
UseThreads(numberOfOperations);
sw.Stop();
Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds); sw.Reset();
sw.Start();
UseThreadPool(numberOfOperations);
sw.Stop();
Console.WriteLine("Execution time using threadPool: {0}", sw.ElapsedMilliseconds);
} static void UseThreads(int numberOfOperations)
{
using (var countdown = new CountdownEvent(numberOfOperations))
{
Console.WriteLine("Scheduling work by creating threads");
for (int i = ; i < numberOfOperations; i++)
{
var thread = new Thread(() => {
Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(TimeSpan.FromSeconds(0.1));
countdown.Signal();
});
thread.Start();
}
countdown.Wait();
Console.WriteLine();
}
} static void UseThreadPool(int numberOfOperations)
{
using (var countdown = new CountdownEvent(numberOfOperations))
{
Console.WriteLine("Starting work on a threadpool");
for (int i = ; i < numberOfOperations; i++)
{
ThreadPool.QueueUserWorkItem(_ => {
Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(TimeSpan.FromSeconds(0.1));
countdown.Signal();
});
}
countdown.Wait();
Console.WriteLine();
}
}
}
}
Program
ThreadPool类(线程池)的更多相关文章
- C# ThreadPool类(线程池)
CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程.线程池初始化时是没有线程的,线程池里的线程的初始化与其他线程一样,但是在完成任务以后,该线 ...
- Android 应用开发 之通过AsyncTask与ThreadPool(线程池)两种方式异步加载大量数据的分析与对比--转载
在加载大量数据的时候,经常会用到异步加载,所谓异步加载,就是把耗时的工作放到子线程里执行,当数据加载完毕的时候再到主线程进行UI刷新.在数据量非常大的情况下,我们通常会使用两种技术来进行异步加载,一 ...
- Thread(线程)和ThreadPool(线程池) Thread回调与返回值
Thread(线程) Thread开启线程:接收一个参数 TestClass tc = new TestClass(); //没有返回值,有一个object类型的参数的委托:两种写法. Paramet ...
- ThreadPool(线程池) in .Net
本文来自:http://rickie.cnblogs.com/archive/2004/11/23/67275.html 在多线程的程序中,经常会出现两种情况.一种情况下,应用程序中的线程把大部分的时 ...
- 模仿.Net ThreadPool的线程池控件
http://www.2ccc.com/btdown.asp?articleid=5953 ftp://download:S3cirpYW3DoR@www.2ccc.com/vcl/system/20 ...
- Java线程池的原理及几类线程池的介绍
刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...
- php Pthread 多线程 (六) Pool类 线程池
Pool对象是多个Worker对象的容器,同时也是它们的控制器,对Worker功能更高抽象. 比如Worker是河,而线程是运行在河里的船.Pool则是管理着多条河. <?php //继承Col ...
- 深入理解java:2.3.6. 并发编程concurrent包 之管理类---线程池
我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁 ...
- 线程池ThreadPool知识碎片和使用经验速记
ThreadPool(线程池)大概的工作原理是,初始时线程池中创建了一些线程,当应用程序需要使用线程池中的线程进行工作,线程池将会分配一个线程,之后到来的请求,线程池都会尽量使用池中已有的这个线程进行 ...
随机推荐
- 对spring控制反转以及依赖注入的理解
一.说到依赖注入(控制反转),先要理解什么是依赖. Spring 把相互协作的关系称为依赖关系.假如 A组件调用了 B组件的方法,我们可称A组件依赖于 B组件. 二.什么是依赖注入. 在传统的程序设计 ...
- 访问sharepoint站点自动使用当前用户名和密码登录
https://blog.csdn.net/zw_2011/article/details/7417123 1.把sharepoint站点添入可信站点. 点击菜单栏“工具”——〉“Internet ...
- acdream 1430 SETI 后缀数组+height分组
这题昨天比赛的时候逗了,后缀想不出来,由于n^2的T了,就没往后缀数组想--并且之后解题的人又说用二分套二分来做.然后就更不会了-- 刚才看了题解,唉--原来题讲解n^2的也能够过,然后就--这样了! ...
- html5如何实现元素拖放
html5如何实现元素拖放 一.总结 一句话总结:参考文档里面有各种想象不到的好东西.一边允许拖放,一边接收拖放,一边传递数据,一边接收数据.拖放过程还要防止拖放以默认方式(链接)打开. 1.html ...
- Erlang 杂记
学习Erlang的时候在书的留白处随手记录了一些东西,还有一些记录在了demo的注释里面,今天抽时间整理出来了一部分,分享一下. Erlang的设计哲学是为每一个独立的事件创建一个新进程. Erlan ...
- 【t003】string
Time Limit: 1 second Memory Limit: 50 MB [问题描述] 设有字符串X,我们称在X的头尾及中间插入任意多个空格后构成的新字符串为X的扩展串,如字符串X为" ...
- SpringBoot使用jsp作为视图模板&常规部署
springboot其实并不推荐使用jsp作为视图模板,其默认采用Thymeleaf作为模板,出于对其没有研究,故考虑目前阶段仍然使用jsp作为视图模板.下面就展开实践案例过程: 1.首先创建一个js ...
- 80. Domino Internet Password
Internet口令保存在Domino文件夹的个人文档的HTTPPassword域中,和文档中的username一起用于藉各种Internet协议訪问Dominoserver时的校验,最经常使用的就是 ...
- Android Studio运行main方法
这样想做一些测试就很简单了 实现步骤如下: 1.当前项目右键->new->Module->Java Library 2.修改你创建javaLib的build.gradle文件 改为( ...
- UWP 和 WPF 对比
原文:UWP 和 WPF 对比 本文告诉大家 UWP 和 WPF 的不同. 如果在遇到技术选择或者想和小伙伴吹的时候可以让他以为自己很厉害,那么请继续看. 如果在看这文章还不知道什么是 UWP 和 W ...