11线程池的使用
- 格式:docx
- 大小:78.03 KB
- 文档页数:15
python线程池的用法Python线程池的用法引言:在并发编程中,线程是一种重要的工具。
然而,创建和管理线程本身是有成本的。
为了降低线程创建和销毁的开销,提高并发程序的性能,我们可以使用线程池。
本文将介绍Python中线程池的用法,希望能帮助读者更好地理解和使用线程池。
一、什么是线程池?线程池是一种用于管理和调度线程的技术。
与每次需要执行任务时都创建和销毁线程不同,线程池会预先创建一组线程,并将这些线程放入一个等待被调度的任务队列中。
当有任务需要执行时,线程池从任务队列中获取一个线程,并分配给任务执行。
完成任务后,线程并不销毁,而是重新放回线程池,等待下一个任务。
这样,我们可以重复利用线程,减少线程创建和销毁的开销,从而提高程序性能。
二、为什么使用线程池?使用线程池可以带来以下几个好处:1. 减少线程创建和销毁的开销:线程创建和销毁是有成本的,频繁地创建和销毁线程会降低程序的性能。
线程池通过预先创建一组线程,并重复利用这些线程,减少了线程创建和销毁的次数,从而降低了成本。
2. 控制线程数量:当任务数量较大时,过多的线程会导致系统资源消耗过大,甚至引发内存溢出等问题。
线程池可以通过控制线程的最大数量来避免这些问题,提高程序的稳定性。
3. 提高程序响应速度:线程池充分利用了线程的并行执行能力,可以同时执行多个任务,提高程序的响应速度。
4. 线程重用:线程池中的线程可以重复利用,避免了频繁创建和销毁线程带来的开销,提高了程序的效率。
三、Python线程池的实现方式在Python中,可以使用内置的concurrent.futures模块来创建和管理线程池。
concurrent.futures模块提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,分别用于创建线程池和进程池。
1. 创建线程池我们首先需要导入concurrent.futures模块,然后通过ThreadPoolExecutor 类来创建线程池。
线程池的运行流程线程池是一种用来管理和复用线程的技术,它可以有效地提高多线程程序的性能和资源利用率。
线程池通过预先创建一定数量的线程,并将任务分配给这些线程来处理,避免了不断创建和销毁线程的开销。
线程池的运行流程如下:1. 线程池的创建:线程池的创建需要确定线程池的大小、任务队列的长度、线程的优先级等参数。
可以通过ThreadPoolExecutor类创建线程池,其中ThreadPoolExecutor构造函数的参数可以指定线程池的大小、任务队列的长度等。
2.初始化线程池:创建线程池之后,线程池会创建一定数量的线程,这些线程处于等待状态,等待任务的到来。
线程池还会创建一个任务队列,用来存放待执行的任务。
3. 提交任务:当需要执行任务时,可以通过execute方法提交任务到线程池。
线程池会将任务放入任务队列中,等待线程来执行。
如果任务队列已满,线程池会根据配置的策略进行处理,比如直接丢弃任务、抛出异常等。
4.选择线程执行任务:线程池会从任务队列中选择一个任务,然后从线程池中选择一个空闲的线程来执行任务。
线程池可以根据多种策略来选择线程,比如先进先出、最近未使用等。
5.执行任务:选中线程开始执行任务。
线程执行任务期间,会不断从任务队列中获取任务并执行,直到任务队列为空或线程池被关闭。
6.线程复用:任务执行完毕之后,线程不会被销毁,而是回到线程池中,等待新的任务。
这样可以减少线程的创建和销毁的开销,提高了线程的复用率。
7.监控和管理:线程池通常会提供一些监控和管理的功能,比如可以监控线程池的运行状态、线程的活动数、任务队列的长度等,还可以根据需求进行动态调整线程池的大小、任务队列的长度等。
8. 关闭线程池:当不再需要线程池时,可以调用shutdown方法来关闭线程池。
关闭线程池后,线程池不再接受新的任务,但会继续执行已提交的任务。
可以通过awaitTermination方法等待线程池中所有任务执行完毕,然后关闭线程池。
java11的线程池写法在Java 11中,线程池的写法可以通过使用`Executors`工厂类来创建不同类型的线程池。
下面我将介绍一些常见的线程池写法。
1. 创建固定大小的线程池:java.ExecutorService executor =Executors.newFixedThreadPool(5);这将创建一个固定大小为5的线程池,线程池中的线程数量始终保持不变,当有新任务提交时,如果线程池中有空闲线程,则立即执行,如果没有,则新任务会被暂存在一个任务队列中,待有线程空闲时再执行。
2. 创建单线程的线程池:java.ExecutorService executor =Executors.newSingleThreadExecutor();这将创建一个只有一个工作线程的线程池,所有任务按照它们被提交的顺序依次执行。
3. 创建可缓存的线程池:java.ExecutorService executor =Executors.newCachedThreadPool();这将创建一个可根据需要创建新线程的线程池,而且在先前构建的线程可用时将重用它们。
如果线程在60秒内未被使用,则将终止并从缓存中移除。
4. 创建定时执行任务的线程池:java.ScheduledExecutorService executor =Executors.newScheduledThreadPool(3);这将创建一个定时执行任务的线程池,可以在给定的延迟之后或者周期性执行任务。
在使用完线程池后,需要调用`shutdown()`方法来关闭线程池: java.executor.shutdown();这将拒绝新任务的提交,等待已经提交的任务执行完成(包括等待队列中的任务),并且不接受新的任务。
以上是在Java 11中常见的线程池写法,通过选择合适的线程池类型,可以更好地满足不同场景下的需求。
线程池的使用和运行流程什么是线程池?线程池是一种常用的并发编程技术,通过预先创建一组线程,并将多个任务分配给这些线程来执行,以提高程序的性能和资源利用率。
线程池可以管理和控制线程的创建、执行和销毁,减少了线程创建和销毁的开销,同时也能够有效地控制线程数量,避免资源过度消耗问题。
线程池的优势使用线程池有以下优势:1.降低线程创建和销毁的开销:线程池在初始化时创建了一组线程,并在需要执行任务时重复利用这些线程,避免了频繁创建和销毁线程的开销。
2.控制线程数量:线程池可以设定最大线程数,防止线程过多导致系统资源耗尽,并可以根据任务负载动态调整线程池的大小。
3.提高程序性能和响应速度:线程池可以通过并发执行多个任务,从而提高程序的执行效率和响应速度。
4.提供线程管理和监控功能:线程池可以提供对线程的管理和监控,例如线程的状态、线程执行的任务数等。
线程池的使用步骤使用线程池可以按照以下步骤进行:1.创建线程池对象:使用线程池需要创建一个线程池对象,可以使用内置的线程池类或自定义线程池类进行创建。
2.提交任务:将需要执行的任务提交给线程池。
任务可以是实现Runnable接口或Callable接口的类。
3.执行任务:线程池会从池中选取一个线程来执行提交的任务,直到任务全部执行完毕或线程池关闭。
4.关闭线程池:当不再需要线程池时,需要显式地关闭线程池,释放资源。
可以调用线程池的shutdown()方法来关闭线程池。
线程池的运行流程线程池的运行流程如下:1.线程池初始化:在创建线程池对象时,需要根据需要设定线程池的参数,例如线程池的大小、任务队列的大小、拒绝策略等。
2.任务提交:将需要执行的任务提交给线程池,线程池将任务存放在任务队列中。
3.任务执行:线程池从任务队列中取出任务,并选择一个线程来执行任务。
线程执行完任务后,会继续从任务队列中取任务执行,直到任务队列为空。
4.线程管理:线程池会管理线程的状态和生命周期。
线程池应用场景随着计算机技术的不断发展,多线程编程已经成为现代软件开发中的重要组成部分。
然而,管理线程的创建和销毁是一项复杂而耗费资源的任务。
为了解决这个问题,线程池应运而生。
线程池是一种管理和复用线程的机制,它可以提高程序的性能和可伸缩性,并减少资源的浪费。
线程池的应用场景非常广泛,下面将介绍几个常见的应用场景。
1. Web服务器在Web服务器中,每个请求都需要创建一个线程来处理。
如果每个请求都创建一个新线程,系统的性能将会受到严重影响。
而使用线程池,可以事先创建一定数量的线程,并将请求分配给这些线程来处理。
这样可以避免频繁创建和销毁线程的开销,提高系统的响应速度和并发处理能力。
2. 数据库连接池在数据库应用中,每个数据库连接都需要占用系统资源。
如果每次操作都创建一个新的数据库连接,系统的性能将会受到限制。
而使用线程池,可以事先创建一定数量的数据库连接,并将数据库操作分配给这些连接来执行。
这样可以避免频繁创建和销毁数据库连接的开销,提高系统的并发处理能力和资源利用率。
3. 并发任务处理在一些需要大量并发处理的场景中,线程池也发挥着重要作用。
例如,一个电商网站需要同时处理多个用户的订单,每个订单都需要进行一系列的处理操作,包括库存检查、支付验证、物流跟踪等。
使用线程池,可以将每个订单的处理任务分配给线程池中的线程来执行,从而实现并发处理,提高系统的吞吐量和响应速度。
4. 定时任务调度在一些需要定时执行任务的场景中,线程池也能发挥重要作用。
例如,一个定时任务需要每隔一段时间执行一次,如果每次都创建一个新线程来执行任务,会导致系统资源的浪费。
而使用线程池,可以事先创建一定数量的线程,并定时调度任务的执行。
这样可以避免频繁创建和销毁线程的开销,提高系统的定时任务执行效率。
总结起来,线程池是一种管理和复用线程的机制,可以提高程序的性能和可伸缩性,并减少资源的浪费。
它在Web服务器、数据库连接池、并发任务处理和定时任务调度等场景中都有广泛的应用。
java线程池用法Java线程池是Java中的一个重要概念,它可以帮助我们更好地管理线程,提高程序的性能和可靠性。
本文将介绍Java线程池的用法,包括线程池的创建、使用和销毁等方面。
一、线程池的创建Java线程池的创建非常简单,只需要使用ThreadPoolExecutor类即可。
ThreadPoolExecutor类是Java中的一个线程池类,它提供了一些方法来创建和管理线程池。
下面是一个简单的线程池创建示例: ```javaExecutorService executor = Executors.newFixedThreadPool(10);```上面的代码创建了一个固定大小为10的线程池。
这个线程池可以同时执行10个任务,如果有更多的任务需要执行,它们将会被放入一个队列中等待执行。
二、线程池的使用线程池的使用非常简单,只需要将任务提交给线程池即可。
下面是一个简单的线程池使用示例:```javaexecutor.submit(new Runnable() {public void run() {// 执行任务}});```上面的代码将一个Runnable对象提交给线程池,线程池会自动分配一个线程来执行这个任务。
如果线程池中没有空闲的线程,这个任务将会被放入队列中等待执行。
三、线程池的销毁线程池的销毁非常重要,如果不及时销毁线程池,会导致程序的性能和可靠性下降。
下面是一个简单的线程池销毁示例:```javaexecutor.shutdown();```上面的代码将会销毁线程池,它会等待所有任务执行完毕后再销毁线程池。
如果你想立即销毁线程池,可以使用下面的代码:```javaexecutor.shutdownNow();```上面的代码会立即销毁线程池,它会尝试中断所有正在执行的任务。
四、线程池的优点Java线程池有很多优点,下面是一些主要的优点:1. 提高程序的性能:线程池可以重复利用线程,避免了线程的创建和销毁,从而提高了程序的性能。
Java线程池使⽤和常⽤参数多线程问题:1、java中为什么要使⽤多线程使⽤多线程,可以把⼀些⼤任务分解成多个⼩任务来执⾏,多个⼩任务之间互不影像,同时进⾏,这样,充分利⽤了cpu资源。
2、java中简单的实现多线程的⽅式继承Thread类,重写run⽅法;12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28class MyTread extends Thread{public void run() { System.out.println(Thread.currentThread().getName());}}实现Runable接⼝,实现run⽅法;class MyRunnable implements Runnable{ public void run() { System.out.println(Thread.currentThread().getName()); }}class ThreadTest { public static void main(String[] args) { MyTread thread = new Mythread(); thread.start(); //开启⼀个线程 MyRunnable myRunnable = new MyRunnable(); Thread runnable = new Thread(myRunnable); runnable.start(); //开启⼀个线程 }}3、java线程的状态创建:当new了⼀个线程,并没有调⽤start之前,线程处于创建状态;就绪:当调⽤了start之后,线程处于就绪状态,这是,线程调度程序还没有设置执⾏当前线程;运⾏:线程调度程序执⾏到线程时,当前线程从就绪状态转成运⾏状态,开始执⾏run⽅法⾥边的代码;阻塞:线程在运⾏的时候,被暂停执⾏(通常等待某项资源就绪后在执⾏,sleep、wait可以导致线程阻塞),这是该线程处于阻塞状态;死亡:当⼀个线程执⾏完run⽅法⾥边的代码或调⽤了stop⽅法后,该线程结束运⾏4、为什么要引⼊线程池当我们需要的并发执⾏线程数量很多时,且每个线程执⾏很短的时间就结束了,这样,我们频繁的创建、销毁线程就⼤⼤降低了⼯作效率(创建和销毁线程需要时间、资源)。
如何通过线程池优化多线程性能多线程编程是提高程序性能的有效方式之一,它可以将任务并行化处理,充分利用多核处理器的计算能力。
然而,多线程编程也存在一些问题,比如线程创建和销毁的开销,线程数量过多导致资源竞争等。
为了解决这些问题,我们可以使用线程池来优化多线程性能。
一、线程池的概念和作用线程池是一种管理和复用线程的机制。
它通过预先创建一定数量的线程,并将任务分配给这些线程来执行,避免了频繁创建和销毁线程的开销。
线程池还可以控制并发线程的数量,防止资源竞争和过载。
二、线程池的使用方法1. 创建线程池Java提供了一个内置的线程池实现类ThreadPoolExecutor,我们可以通过它来创建线程池。
常用的构造方法有以下几种:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)corePoolSize:核心线程数,代表线程池中可以同时执行的线程数量。
这些线程会一直存在,即使它们处于空闲状态。
maximumPoolSize:最大线程数,代表线程池中允许的最大线程数量。
keepAliveTime:非核心线程的空闲超时时间,即当线程池中的线程数量大于corePoolSize时,空闲线程在这段时间后会被销毁。
unit:keepAliveTime的时间单位。
workQueue:任务队列,用于存放尚未执行的任务。
2. 提交任务线程池创建好后,我们可以通过ThreadPoolExecutor的submit()方法将任务提交给线程池。
任务可以是Runnable对象或Callable对象。
submit(Runnable task):提交Runnable任务。
submit(Callable<T> task):提交Callable任务,并返回一个Future对象,通过该对象可以获取任务执行结果。
JAVA线程池的创建与使用线程池是Java并发编程中的一种机制,它可以提供一组可重用的线程,用于执行多个任务。
通过使用线程池,可以避免频繁地创建和销毁线程,从而减少了系统资源的消耗,提高了程序的性能和响应速度。
本文将介绍Java线程池的创建与使用,包括线程池的概念、创建线程池和使用线程池的注意事项等。
一、线程池的概念线程池是一种管理和复用线程的机制,它可以提供一组线程,用于执行多个任务。
在线程池中,有一个线程池管理器负责管理池中的线程,并根据任务的数量和优先级来动态调整线程的数量。
线程池中的线程可以重复使用,当一个任务执行完毕后,不会立即销毁线程,而是放回线程池中,供其他任务使用。
这样可以减少线程的创建和销毁次数,提高系统的性能和响应速度。
二、创建线程池Java提供了ThreadPoolExecutor类,用于创建和管理线程池。
ThreadPoolExecutor类的构造函数接收一些参数,用于配置线程池的行为。
下面是一个创建线程池的示例代码:```javaExecutorService executorService =Executors.newFixedThreadPool(10);```上述代码中,通过Executors类的静态方法newFixedThreadPool来创建一个固定大小的线程池,线程池的大小为10。
创建完线程池后,可以使用submit方法提交任务给线程池执行,如下所示:```javaexecutorService.submit(new Runnablepublic void run//任务的具体逻辑代码}});```此外,还可以使用execute方法来提交任务给线程池执行,两者的区别主要是前者可以返回一个Future对象,用于获取任务的执行结果。
三、使用线程池的注意事项使用线程池需要注意以下几点:1. 根据任务的特点和需求选择合适的线程池类型。
Java提供了几种常用的线程池类型,如FixedThreadPool、CachedThreadPool和ScheduledThreadPool等,根据任务的数量和种类选择合适的线程池。
线程池实际案例使用全文共四篇示例,供读者参考第一篇示例:随着互联网和移动互联网的发展,线程池成为了开发中常用的一种机制。
线程池的作用是管理、控制和复用线程,避免了频繁创建和销毁线程的开销,提高了并发性能和资源利用率。
在实际的开发中,线程池被广泛应用于网络编程、并发处理、数据库连接池等场景。
下面我们来看一些线程池的实际案例使用。
一、网络编程在网络编程中,通常会使用线程池来处理多个客户端的请求。
比如一个简单的网络服务器,可以通过线程池来处理客户端发来的请求,每个客户端连接后就会分配一个线程来处理。
这样可以避免每个客户端连接都创建一个线程,提高服务器的性能和稳定性。
二、并发处理在一些需要并发处理大量任务的场景中,线程池也非常有用。
比如在一个电商网站中,有大量的订单需要处理,可以使用线程池来对订单进行并发处理,每个订单分配一个线程来处理,同时限制线程数量,避免资源被耗尽。
这样可以提高订单处理的效率,并且保证系统的稳定性。
三、数据库连接池数据库连接通常是一种昂贵的资源,为了减少数据库连接的开销,可以使用线程池来管理数据库连接。
通过线程池可以复用数据库连接,避免频繁地建立和断开连接。
在实际开发中,我们可以使用开源框架如c3p0、Druid等来实现数据库连接池,这些框架提供了丰富的配置选项和监控功能,可以方便地管理数据库连接。
四、任务调度线程池也可以用于任务调度,比如定时任务、周期性任务等。
我们可以使用Java自带的ScheduledThreadPoolExecutor来实现这些功能,通过线程池来管理定时任务的执行。
这样可以避免手动创建线程来执行定时任务,提高了代码的可维护性和可读性。
以上是线程池在实际开发中的一些应用场景,线程池在提高系统性能、减少资源开销、保证系统稳定性等方面发挥了重要作用。
在使用线程池时,需要根据具体的业务需求和场景来合理配置线程池的参数,避免线程池过大导致资源浪费,或者线程池过小导致任务阻塞。
第11章线程池的使用第8章讲述了如何使用让线程保持用户方式的机制来实现线程同步的方法。
用户方式的同步机制的出色之处在于它的同步速度很快。
如果关心线程的运行速度,那么应该了解一下用户方式的同步机制是否适用。
到目前为止,已经知道创建多线程应用程序是非常困难的。
需要会面临两个大问题。
一个是要对线程的创建和撤消进行管理,另一个是要对线程对资源的访问实施同步。
为了对资源访问实施同步,Wi n d o w s提供了许多基本要素来帮助进行操作,如事件、信标、互斥对象和关键代码段等。
这些基本要素的使用都非常方便。
为了使操作变得更加方便,唯一的方法是让系统能够自动保护共享资源。
不幸的是,在Wi n d o w s提供一种让人满意的保护方法之前,我们已经有了一种这样的方法。
在如何对线程的创建和撤消进行管理的问题上,人人都有自己的好主意。
近年来,我自己创建了若干不同的线程池实现代码,每个实现代码都进行了很好的调整,以便适应特定环境的需要。
M i c r o s o f t公司的Windows 2000提供了一些新的线程池函数,使得线程的创建、撤消和基本管理变得更加容易。
这个新的通用线程池并不完全适合每一种环境,但是它常常可以适合你的需要,并且能够节省大量的程序开发时间。
新的线程池函数使你能够执行下列操作:• 异步调用函数。
• 按照规定的时间间隔调用函数。
• 当单个内核对象变为已通知状态时调用函数。
• 当异步I / O请求完成时调用函数。
为了完成这些操作,线程池由4个独立的部分组成。
表11 - 1显示了这些组件并描述了控制其行为特性的规则。
表11-1 线程池的组件及其行为特性当进程初始化时,它并不产生与这些组件相关联的任何开销。
但是,一旦新线程池函数之一被调用时,就为进程创建某些组件,并且其中有些组件将被保留,直到进程终止运行为止。
如你所见,使用线程池所产生的开销并不小。
相当多的线程和内部数据结构变成了你的进程的一个组成部分。
因此必须认真考虑线程池能够为你做什么和不能做什么,不要盲目地使用这些函数。
好了,上述说明已经足够了。
下面让我们来看一看这些函数能够做些什么。
11.1 方案1:异步调用函数假设有一个服务器进程,该进程有一个主线程,正在等待客户机的请求。
当主线程收到该请求时,它就产生一个专门的线程,以便处理该请求。
这使得应用程序的主线程循环运行,并等待另一个客户机的请求。
这个方案是客户机/服务器应用程序的典型实现方法。
虽然它的实现方法非常明确,但是也可以使用新线程池函数来实现它。
当服务器进程的主线程收到客户机的请求时,它可以调用下面这个函数:BOOL QueueUserWorkItem(PTHREAD_START_ROUTINE pfnCallback,PVOID pvContext,ULONG dwFlags);该函数将一个“工作项目”排队放入线程池中的一个线程中并且立即返回。
所谓工作项目是指一个(用p f n C a l l b a c k参数标识的)函数,它被调用并传递单个参数p v C o n t e x t。
最后,线程池中的某个线程将处理该工作项目,导致函数被调用。
所编的回调函数必须采用下面的原型:DWORD WINAPI WorkItemFunc(PVOID pvContext);尽管必须使这个函数的原型返回D W O R D,但是它的返回值实际上被忽略了。
注意,你自己从来不调用C r e a t e T h r e a d。
系统会自动为你的进程创建一个线程池,线程池中的一个线程将调用你的函数。
另外,当该线程处理完客户机的请求之后,该线程并不立即被撤消。
它要返回线程池,这样它就可以准备处理已经排队的任何其他工作项目。
你的应用程序的运行效率可能会变得更高,因为不必为每个客户机请求创建和撤消线程。
另外,由于线程与完成端口相关联,因此可以同时运行的线程数量限制为C P U数量的两倍。
这就减少了线程的上下文转移的开销。
该函数的内部运行情况是,Q u e u e U s e r Wo r k I t e m检查非I / O组件中的线程数量,然后根据负荷量(已排队的工作项目的数量)将另一个线程添加给该组件。
接着Q u e u e U s e r Wo r k I t e m执行对P o s t Q u e u e d C o m p l e t i o n S t a t u s的等价调用,将工作项目的信息传递给I / O完成端口。
最后,在完成端口上等待的线程取出信息(通过调用G e t Q u e u e d C o m p l e t i o n S t a t u s),并调用函数。
当函数返回时,该线程再次调用G e t Q u e u e d C o m p l e t i o n S t a t u s,以便等待另一个工作项目。
线程池希望经常处理异步I / O请求,即每当线程将一个I / O请求排队放入设备驱动程序时,便要处理异步I / O请求。
当设备驱动程序执行该I / O时,请求排队的线程并没有中断运行,而是继续执行其他指令。
异步I / O是创建高性能可伸缩的应用程序的秘诀,因为它允许单个线程处理来自不同客户机的请求。
该线程不必顺序处理这些请求,也不必在等待I / O请求运行结束时中断运行。
但是,Wi n d o w s对异步I / O请求规定了一个限制,即如果线程将一个异步I / O请求发送给设备驱动程序,然后终止运行,那么该I / O请求就会丢失,并且在I / O请求运行结束时,没有线程得到这个通知。
在设计良好的线程池中,线程的数量可以根据客户机的需要而增减。
因此,如果线程发出一个异步I / O请求,然后因为线程池缩小而终止运行,那么该I / O请求也会被撤消。
因为这种情况实际上并不是你想要的,所以你需要一个解决方案。
如果你想要给发出异步I / O请求的工作项目排队,不能将该工作项目插入线程池的非I / O组件中。
必须将该工作项目放入线程池的I / O组件中进行排队。
该I / O组件由一组线程组成,如果这组线程还有尚未处理的I / O请求,那么它们决不能终止运行。
因此你只能将它们用来运行发出异步I / O请求的代码。
若要为I / O组件的工作项目进行排队,仍然必须调用Q u e u e U s e r Wo r k I t e m函数,但是可以为d w F l a g s参数传递W T _ E X E C U T E I N I O T H R E A D。
通常只需传递W T _ E X E C U T E D E FA U LT(定义为0),这使得工作项目可以放入非I / O组件的线程中。
Wi n d o w s提供的函数(如R e g N o t i f y C h a n g e K e y Va l u e)能够异步执行与非I / O相关的任务。
这些函数也要求调用线程不能终止运行。
如果想使用永久线程池的线程来调用这些函数中的一个,可以使用W T _ E X E C U T E I N P E R S I S T E N T T H R E A D标志,它使定时器组件的线程能够执行已排队的工作项目回调函数。
由于定时器组件的线程决不会终止运行,因此可以确保最终发生异步操作。
应该保证回调函数不会中断,并且保证它能迅速执行,这样,定时器组件的线程就不会受到不利的影响。
设计良好的线程池也必须设法保证线程始终都能处理各个请求。
如果线程池包含4个线程,并且有1 0 0个工作项目已经排队,每次只能处理4个工作项目。
如果一个工作项目只需要几个毫秒来运行,那么这是不成问题的。
但是,如果工作项目需要运行长得多的时间,那么将无法及时处理这些请求。
当然,系统无法很好地预料工作项目函数将要进行什么操作,但是,如果知道工作项目需要花费很长的时间来运行,那么可以调用Q u e u e U s e r Wo r k I t e m 函数,为它传递W T _ E X E C U T E L O N G F U N C T I O N标志。
该标志能够帮助线程池决定是否要将新线程添加给线程池。
如果线程池中的所有线程都处于繁忙状态,它就会强制线程池创建一个新线程。
因此,如果同时对10 000个工作项目进行了排队(使用W T _ E X E C U T E L O N G F U N C T I O N标志),那么这10 000 个线程就被添加给该线程池。
如果不想创建10 000个线程,必须分开调用Q u e ue U s e r Wo r k I t e m函数,这样某些工作项目就有机会完成运行。
线程池不能对线程池中的线程数量规定一个上限,否则就会发生渴求或死锁现象。
假如有1 00 0 0个排队的工作项目,当第10 001个项目通知一个事件时,这些工作项目将全部中断运行。
如果你已经设置的最大数量为10 000个线程,第10 001个工作项目没有被执行,那么所有的10 000个线程将永远被中断运行。
当使用线程池函数时,应该查找潜在的死锁条件。
当然,如果工作项目函数在关键代码段、信标和互斥对象上中断运行,那么必须十分小心,因为这更有可能产生死锁现象。
始终都应该了解哪个组件(I / O、非I / O、等待或定时器等)的线程正在运行你的代码。
另外,如果工作项目函数位于可能被动态卸载的D L L中,也要小心。
调用已卸载的D L L中的函数的线程将会产生违规访问。
若要确保不卸载带有已经排队的工作项目的D L L,必须对已排队工作项目进行引用计数,在调用Q u e u e U s e r Wo r k I t e m函数之前递增计数器的值,当工作项目函数完成运行时则递减该计数器的值。
只有当引用计数降为0时,才能安全地卸载D L L。
11.2 方案2:按规定的时间间隔调用函数有时应用程序需要在某些时间执行操作任务。
Wi n d o w s提供了一个等待定时器内核对象,因此可以方便地获得基于时间的通知。
许多程序员为应用程序执行的每个基于时间的操作任务创建了一个等待定时器对象,但是这是不必要的,会浪费系统资源。
相反,可以创建一个等待定时器,将它设置为下一个预定运行的时间,然后为下一个时间重置定时器,如此类推。
然而,要编写这样的代码非常困难,不过可以让新线程池函数对此进行管理。
若要调度在某个时间运行的工作项目,首先要调用下面的函数,创建一个定时器队列:HANDLE CreateTimerQueue();定时器队列对一组定时器进行组织安排。
例如,有一个可执行文件控制着若干个服务程序。
每个服务程序需要触发定时器,以帮助保持它的状态,比如客户机何时不再作出响应,何时收集和更新某些统计信息等。
让每个服务程序占用一个等待定时器和专用线程,这是不经济的。