虽然创建线程优于创建进程,尽管如此,多线程服务器仍然存在潜在问题

  • 第一个问题涉及创建线程所需的时间,以及线程在完成其工作后将被丢弃的事实。
  • 第二个问题比较麻烦。如果我们允许在新线程中并发的处理所有传入的请求,而我们还没有对系统中并发活动的线程数量进行限定。无限的线程可能耗尽系统资源,比如CPU时间或内存。

解决这个问题的一个方法是使用线程池

线程池背后的基本思想是:在进程启动时创建多个线程,并将它们放到一个池中,在池中等待工作。当一个服务器接收到一个请求时,它会从这个线程池中唤醒一个线程——如果有的话——并将服务的请求传递给它。一旦线程完成它的服务,它将返回到池并等待更多的工作。如果池中没有可用的线程,则服务器将等待直到一个空闲线程。

线程池的优点

  • 使用一个已有的线程来为请求服务,要远比等待创建一个线程快的多
  • 线程池限制了在任何时候存在的线程数的上限。这对于不能支持大量并发线程的系统尤其重要。

  • 将任务从创建任务的机制中分离出来,允许我们使用不同的策略来运行任务。

池中线程的数量可以根据各种因素进行设置,比如系统CPU的数量,物理内存的大小,或者是并发客户端请求的数量。

很多复杂的线程池架构可以动态的根据使用模式调整池中的线程数。这类结构的优点是在系统低负荷时,减少内存消耗

我们会讨论这样的架构,Apple的Grand Central Dispatch。

Windows API提供了多个与线程池相关的函数。使用线程池API非常类似于用创建线程的Thread_Create()函数。

定义一个作为单独的线程运行的函数:

DWORD WINAPI PoolFunction(AVOID Param) {
/*
* this function runs as a separate thread.
*/
}

一个指向PoolFunction(见上面代码段)的指针被传递给线程池API中的一个函数,而来自池中的线程将执行这个函数。

QueueUserWorkItem()函数是线程池API中的一个,它需要传递三个参数:

  • LPTHREAD_START_ROUTINE Function:指向作为单独的线程运行的函数的指针。
  • PVOID Param:传递给Function的参数
  • ULONG Flags:指示线程池如何创建和管理线程执行的标志。

例子:

QueueUserWorkItem(&PoolFunction, NULL, 0);

这会使线程池中的线程代表程序员去调用PoolFunction()。在此例子中,我们没有传递给PoolFunction()的参数。因为我们将0指定为标志位,所以我们所提供线程池,并没有专门的线程创建指令。

Java的java.util.concurrent包提供了更好额线程池基础设施。

results matching ""

    No results matching ""