Fromhttp://kb.cnblogs.com/page/42531/

在多线程的程序中,经常会出现两种情况:

一种情况: 应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应

这一般使用ThreadPool(线程池)来解决;

另一种情况:线程平时都处于休眠状态,只是周期性地被唤醒

这一般使用Timer(定时器)来解决;

ThreadPool类提供一个由系统维护的线程池(可以看作一个线程的容器),该容器需要 Windows 2000 以上系统支持,因为其中某些方法调用了只有高版本的Windows才有的API函数。

将线程安放在线程池里,需使用ThreadPool.QueueUserWorkItem()方法,该方法的原型如下:

//将一个线程放进线程池,该线程的Start()方法将调用WaitCallback代理对象代表的函数

public static bool QueueUserWorkItem(WaitCallback);

//重载的方法如下,参数object将传递给WaitCallback所代表的方法

public static bool QueueUserWorkItem(WaitCallback, object);

ThreadPool类是一个静态类,你不能也不必要生成它的对象。而且一旦使用该方法在线程池中添加了一个项目,那么该项目将是无法取消的。

在这里你无需自己建立线程,只需把你要做的工作写成函数,然后作为参数传递给ThreadPool.QueueUserWorkItem()方法就行了,传递的方法就是依靠WaitCallback代理对象,而线程的建立、管理、运行等工作都是由系统自动完成的,你无须考虑那些复杂的细节问题。

ThreadPool 的用法:

首先程序创建了一个ManualResetEvent对象,该对象就像一个信号灯,可以利用它的信号来通知其它线程。

本例中,当线程池中所有线程工作都完成以后,ManualResetEvent对象将被设置为有信号,从而通知主线程继续运行。

ManualResetEvent对象有几个重要的方法:

初始化该对象时,用户可以指定其默认的状态(有信号/无信号);

在初始化以后,该对象将保持原来的状态不变,直到它的Reset()或者Set()方法被调用:

Reset()方法:将其设置为无信号状态;

Set()方法:将其设置为有信号状态。

WaitOne()方法:使当前线程挂起,直到ManualResetEvent对象处于有信号状态,此时该线程将被激活。然后,程序将向线程池中添加工作项,这些以函数形式提供的工作项被系统用来初始化自动建立的线程。当所有的线程都运行完了以后,ManualResetEvent.Set()方法被调用,因为调用了ManualResetEvent.WaitOne()方法而处在等待状态的主线程将接收到这个信号,于是它接着往下执行,完成后边的工作。

ThreadPool 的用法示例:

using System;
using System.Collections;
using System.Threading; namespace ThreadExample
{
//这是用来保存信息的数据结构,将作为参数被传递
public class SomeState
{
   public int Cookie;
   public SomeState(int iCookie)
   {
Cookie = iCookie;
   }
} public class Alpha
{
  public Hashtable HashCount;
  public ManualResetEvent eventX;
  public static int iCount = ;
  public static int iMaxCount = ;
  
public Alpha(int MaxCount)
  {
  HashCount = new Hashtable(MaxCount);
  iMaxCount = MaxCount;
  }   //线程池里的线程将调用Beta()方法
  public void Beta(Object state)
  {
   //输出当前线程的hash编码值和Cookie的值
  Console.WriteLine(" {0} {1} :", Thread.CurrentThread.GetHashCode(),((SomeState)state).Cookie);
Console.WriteLine("HashCount.Count=={0}, Thread.CurrentThread.GetHashCode()=={1}", HashCount.Count, Thread.CurrentThread.GetHashCode());
lock (HashCount)
{
     //如果当前的Hash表中没有当前线程的Hash值,则添加之
     if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode()))
     HashCount.Add (Thread.CurrentThread.GetHashCode(), );
     HashCount[Thread.CurrentThread.GetHashCode()] =
((int)HashCount[Thread.CurrentThread.GetHashCode()])+;
   }
int iX = ;
Thread.Sleep(iX);
//Interlocked.Increment()操作是一个原子操作,具体请看下面说明
Interlocked.Increment(ref iCount); if (iCount == iMaxCount)
{
    Console.WriteLine();
     Console.WriteLine("Setting eventX ");
     eventX.Set();
  }
   }
} public class SimplePool
{
public static int Main(string[] args)
{
Console.WriteLine("Thread Pool Sample:");
bool W2K = false;
int MaxCount = ;//允许线程池中运行最多10个线程
//新建ManualResetEvent对象并且初始化为无信号状态
ManualResetEvent eventX = new ManualResetEvent(false);
Console.WriteLine("Queuing {0} items to Thread Pool", MaxCount);
Alpha oAlpha = new Alpha(MaxCount);
//创建工作项
//注意初始化oAlpha对象的eventX属性
oAlpha.eventX = eventX;
Console.WriteLine("Queue to Thread Pool 0");
try
{
//将工作项装入线程池
//这里要用到Windows 2000以上版本才有的API,所以可能出现NotSupportException异常
ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta), new SomeState());
W2K = true;
}
catch (NotSupportedException)
{
Console.WriteLine("These API's may fail when called on a non-Windows 2000 system.");
W2K = false;
}
if (W2K)//如果当前系统支持ThreadPool的方法.
{
for (int iItem=;iItem < MaxCount;iItem++)
{
//插入队列元素
Console.WriteLine("Queue to Thread Pool {0}", iItem);
ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta), new SomeState(iItem));
}
Console.WriteLine("Waiting for Thread Pool to drain");
//等待事件的完成,即线程调用ManualResetEvent.Set()方法
eventX.WaitOne(Timeout.Infinite,true);
//WaitOne()方法使调用它的线程等待直到eventX.Set()方法被调用
Console.WriteLine("Thread Pool has been drained (Event fired)");
Console.WriteLine();
Console.WriteLine("Load across threads");
foreach(object o in oAlpha.HashCount.Keys)
Console.WriteLine("{0} {1}", o, oAlpha.HashCount[o]);
}
Console.ReadLine();
return ;
}
}
}
}

程序中应该引起注意的地方:

SomeState类是一个保存信息的数据结构,它在程序中作为参数被传递给每一个线程,因为你需要把一些有用的信息封装起来提供给线程,而这种方式是非常有效的。

程序出现的InterLocked类也是专为多线程程序而存在的,它提供了一些有用的原子操作。

原子操作:就是在多线程程序中,如果这个线程调用这个操作修改一个变量,那么其他线程就不能修改这个变量了,这跟lock关键字在本质上是一样的。

输出结果:

Thread Pool Sample:
Queuing items to Thread Pool
Queue to Thread Pool
Queue to Thread Pool
Queue to Thread Pool
Queue to Thread Pool
Queue to Thread Pool
Queue to Thread Pool
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
Queue to Thread Pool
Queue to Thread Pool
Queue to Thread Pool
Queue to Thread Pool
Waiting for Thread Pool to drain
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()==
:
HashCount.Count==, Thread.CurrentThread.GetHashCode()== Setting eventX
Thread Pool has been drained (Event fired) Load across threads

我们应该彻底地分析上面的程序,把握住线程池的本质,理解它存在的意义是什么,这样才能得心应手地使用它。

------------------>>>

C# 线程(五):线程池的更多相关文章

  1. Java线程及线程池状态

    一.Java线程的六种状态 如上图1,JDK定义线程状态是不存在“运行中”状态,但为方便描述过程有些图中会画出运行中的状态. Java线程创建后调用start方法进入就绪状态,被OS调度选中后运行,运 ...

  2. Java多线程面试题:线程锁+线程池+线程同步等

    1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...

  3. Python程序中的线程操作(线程池)-concurrent模块

    目录 Python程序中的线程操作(线程池)-concurrent模块 一.Python标准模块--concurrent.futures 二.介绍 三.基本方法 四.ProcessPoolExecut ...

  4. 线程和线程池的理解与java简单例子

    1.线程 (1)理解,线程是系统分配处理器时间资源的基本单元也是系统调用的基本单位,简单理解就是一个或多个线程组成了一个进程,进程就像爸爸,线程就像儿子,有时候爸爸一个人干不了活就生了几个儿子干活,会 ...

  5. [Java线程] Java线程池ExecutorService

    示例 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.u ...

  6. Http协议、线程、线程池

    Socket模拟服务端运行代码: 1:启动服务端监听的服务,并接受客户端的连接 1.1 创建Socket Socket listenSocket=new Socket(AddressFamily.In ...

  7. 《Android开发艺术探索》读书笔记 (11) 第11章 Android的线程和线程池

    第11章 Android的线程和线程池 11.1 主线程和子线程 (1)在Java中默认情况下一个进程只有一个线程,也就是主线程,其他线程都是子线程,也叫工作线程.Android中的主线程主要处理和界 ...

  8. java 线程、线程池基本应用演示样例代码回想

    java 线程.线程池基本应用演示样例代码回想 package org.rui.thread; /** * 定义任务 * * @author lenovo * */ public class Lift ...

  9. C# 多线程、异步线程、线程池相关知识

    /* 线程池ThreadPool类会在需要时增减池中线程的线程数,直到最大的线程数.池中的最大线程数是可配置的. 在双核CPU中,默认设置为1023个工作线程和1000个I/O线程.也可以指定在创建线 ...

  10. JAVA之旅(十五)——多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止

    JAVA之旅(十五)--多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止 我们接着多线程讲 一.生产者和消费者 什么是生产者和消费者?我们 ...

随机推荐

  1. 161201、常用 SQL Server 规范集锦

    常见的字段类型选择   1.字符类型建议采用varchar/nvarchar数据类型 2.金额货币建议采用money数据类型 3.科学计数建议采用numeric数据类型 4.自增长标识建议采用bigi ...

  2. satis 搭建 Composer 私有库的方法

    安装 satis 命令行下执行: php create-project composer/satis --stability=dev --keep-vcs . 配置 创建 satis.json 文件, ...

  3. PHP易混淆函数的区分

    常量定义自定义常量 常量名区分大小写系统的魔术常量不区分大小写 __DIR__ __dir__变量定义变量名是区分大小写变量名声明时用$符号开头, 而且要符合变量名的命名规则$i;var_dump($ ...

  4. iOS-网址集

    0. 在线工具 http://tool.lu 1. iOS学习笔记汇总链接 https://blog.6ag.cn/533.html 2.iOS开发内购全套图文教程http://mp.weixin.q ...

  5. C# winform 中MessageBox用法大全(附效果图) (转载+说明)

    声明:这篇文章是转载的转载的,由于原作者的博客被关闭 我就不再列出了,提前先说明下,if语句中的判断有些太长,建议提前声明一个变量, DialogResult MsgBoxResult;        ...

  6. 怎样新建Oracle数据库

    新建Oracle数据库三种方法:1.通过运行Oracle Database Configuration Assistant 创建配置或删除数据库(也可在命令行下输入dbca):2.用命令行的方式建立数 ...

  7. 什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append性能好

    如果在编写代码的过程中大量使用+进行字符串评价还是会对性能造成比较大的影响,但是使用的个数在1000以下还是可以接受的,大于10000的话,执行时间将可能超过1s,会对性能产生较大影响.如果有大量需要 ...

  8. JDBC的批量处理数据

    主要用到的方法有: preparedStatement.executeBatch();//积攒的数据执行 preparedStatement.clearBatch();//积攒的清除掉 prepare ...

  9. Network Saboteur 分类: 搜索 POJ 2015-08-09 19:48 7人阅读 评论(0) 收藏

    Network Saboteur Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 10147 Accepted: 4849 Des ...

  10. servlet中Java连接数据库后的基本操作

    servlet中Java连接数据库后的基本操作 在eclipse中新建一个工程:login 在Server中新建一个服务器,基本的操作不用说了,在前两天的笔记中可以找到; 需要知道数据库的用户名和密码 ...