VC实现线程池

简介: 这两天在做关于网络蜘蛛的程序,希望可以通过线程池来提高程序的性能,网上搜索了一下,看到这方面的东西还不少,跟大家分享一下!~有许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。
这两天在做关于网络蜘蛛的程序,希望可以通过线程池来提高程序的性能,网上搜索了一下,看到这方面的东西还不少,跟大家分享一下!~有许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线程池可以让你更有效地使用线程,它为你的应用程序提供一个由系统管理的工作者线程池。至少会有一个线程来监听放到线程池的所有等待操作,当等待操作完成后,线程池中将会有一个工作者线程来执行相应的回调函数。
  你也可以把没有等待操作的工作项目放到线程池中,用QueueUserWorkItem函数来完成这个工作,把要执行的工作项目函数通过一个参数传递给线程池。工作项目被放到线程池中后,就不能再取消了。
  Timer-queue timers和Registered wait operations也使用线程池来实现。他们的回调函数也放在线程池中。你也可以用BindIOCompletionCallback函数来投递一个异步IO操作,在IO完成端口上,回调函数也是由线程池线程来执行。
  当第一次调用QueueUserWorkItem函数或者BindIOCompletionCallback函数的时候,线程池被自动创建,或者Timer-queue timers或者Registered wait operations放入回调函数的时候,线程池也可以被创建。线程池可以创建的线程数量不限,仅受限于可用的内存,每一个线程使用默认的初始堆栈大小,运行在默认的优先级上。
  线程池中有两种类型的线程:IO线程和非IO线程。IO线程等待在可告警状态,工作项目作为APC放到IO线程中。如果你的工作项目需要线程执行在可警告状态,你应该将它放到IO线程。
  非IO工作者线程等待在IO完成端口上,使用非IO线程比IO线程效率更高,也就是说,只要有可能的话,尽量使用非IO线程。IO线程和非IO线程在异步IO操作没有完成之前都不会退出。然而,不要在非IO线程中发出需要很长时间才能完成的异步IO请求。
  正确使用线程池的方法是,工作项目函数以及它将会调用到的所有函数都必须是线程池安全的。安全的函数不应该假设线程是一次性线程的或者是永久线程。一般来说,应该避免使用线程本地存储和发出需要永久线程的异步IO调用,比如说RegNotifyChangeKeyValue函数。如果需要在永久线程中执行这样的函数的话,可以给QueueUserWorkItem传递一个选项WT_EXECUTEINPERSISTENTTHREAD。
  注意,线程池不能兼容COM的单线程套间(STA)模型。
  
      为了更深入地讲解操作系统实现的线程池的优越性,我们首先尝试着自己实现一个简单的线程池模型。
  
      代码如下:
   /************************************************************************/
   /* Test Our own thread pool.                                            */
   /************************************************************************/
  
  typedef  struct _THREAD_POOL
   {
      HANDLE QuitEvent;
      HANDLE WorkItemSemaphore;
  
      LONG WorkItemCount;
      LIST_ENTRY WorkItemHeader;
      CRITICAL_SECTION WorkItemLock;
  
      LONG ThreadNum;
      HANDLE *ThreadsArray;
  
  }
THREAD_POOL, *PTHREAD_POOL;
  
  typedef VOID (*WORK_ITEM_PROC)(PVOID Param);
  
  typedef  struct _WORK_ITEM
   {
      LIST_ENTRY List;
  
      WORK_ITEM_PROC UserProc;
      PVOID UserParam;
      
  }
WORK_ITEM, *PWORK_ITEM;
  
  
  DWORD WINAPI WorkerThread(PVOID pParam)
   {
      PTHREAD_POOL pThreadPool = (PTHREAD_POOL)pParam;
      HANDLE Events[2];
      
      Events[0] = pThreadPool->QuitEvent;
      Events[1] = pThreadPool->WorkItemSemaphore;
  
      for(;;)
      {
          DWORD dwRet = WaitForMultipleObjects(2, Events, FALSE, INFINITE);
  
          if(dwRet == WAIT_OBJECT_0)
              break;
  
          //
          
// execute user's proc.
          
//
  
          else if(dwRet == WAIT_OBJECT_0 +1)
          {
              PWORK_ITEM pWorkItem;
              PLIST_ENTRY pList;
  
              EnterCriticalSection(&pThreadPool->WorkItemLock);
              _ASSERT(!IsListEmpty(&pThreadPool->WorkItemHeader));
              pList = RemoveHeadList(&pThreadPool->WorkItemHeader);
              LeaveCriticalSection(&pThreadPool->WorkItemLock);
  
              pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);
              pWorkItem->UserProc(pWorkItem->UserParam);
  
              InterlockedDecrement(&pThreadPool->WorkItemCount);
              free(pWorkItem);
          }

  
          else
          {
              _ASSERT(0);
              break;
          }

      }

  
      return 0;
  }

  
  BOOL InitializeThreadPool(PTHREAD_POOL pThreadPool, LONG ThreadNum)
   {
      pThreadPool->QuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
      pThreadPool->WorkItemSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
      pThreadPool->WorkItemCount = 0;
      InitializeListHead(&pThreadPool->WorkItemHeader);
      InitializeCriticalSection(&pThreadPool->WorkItemLock);
      pThreadPool->ThreadNum = ThreadNum;
      pThreadPool->ThreadsArray = (HANDLE*)malloc(sizeof(HANDLE) * ThreadNum);
  
      for(int i=0; i<ThreadNum; i++)
      {
          pThreadPool->ThreadsArray[i] = CreateThread(NULL, 0, WorkerThread, pThreadPool, 0, NULL);
      }

  
      return TRUE;
  }

  
  VOID DestroyThreadPool(PTHREAD_POOL pThreadPool)
   {
      SetEvent(pThreadPool->QuitEvent);
  
      for(int i=0; i<pThreadPool->ThreadNum; i++)
      {
          WaitForSingleObject(pThreadPool->ThreadsArray[i], INFINITE);
          CloseHandle(pThreadPool->ThreadsArray[i]);
      }

  
      free(pThreadPool->ThreadsArray);
  
      CloseHandle(pThreadPool->QuitEvent);
      CloseHandle(pThreadPool->WorkItemSemaphore);
      DeleteCriticalSection(&pThreadPool->WorkItemLock);
  
      while(!IsListEmpty(&pThreadPool->WorkItemHeader))
      {
          PWORK_ITEM pWorkItem;
          PLIST_ENTRY pList;
          
          pList = RemoveHeadList(&pThreadPool->WorkItemHeader);
          pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);
          
          free(pWorkItem);
      }

  }

  
  BOOL PostWorkItem(PTHREAD_POOL pThreadPool, WORK_ITEM_PROC UserProc, PVOID UserParam)
   {
      PWORK_ITEM pWorkItem = (PWORK_ITEM)malloc(sizeof(WORK_ITEM));
      if(pWorkItem == NULL)
          return FALSE;
  
      pWorkItem->UserProc = UserProc;
      pWorkItem->UserParam = UserParam;
  
      EnterCriticalSection(&pThreadPool->WorkItemLock);
      InsertTailList(&pThreadPool->WorkItemHeader, &pWorkItem->List);
      LeaveCriticalSection(&pThreadPool->WorkItemLock);
  
      InterlockedIncrement(&pThreadPool->WorkItemCount);
       ReleaseSemaphore(pThreadPool->WorkItemSemaphore, 1, NULL);
  
      return TRUE;
  }

  
  VOID UserProc1(PVOID dwParam)
   {
      WorkItem(dwParam);
  }

  
   void TestSimpleThreadPool(BOOL bWaitMode, LONG ThreadNum)
   {
      THREAD_POOL ThreadPool;    
      InitializeThreadPool(&ThreadPool, ThreadNum);
      
      CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
      BeginTime = GetTickCount();
      ItemCount = 20;
  
      for(int i=0; i<20; i++)
      {
          PostWorkItem(&ThreadPool, UserProc1, (PVOID)bWaitMode);
      }

      
      WaitForSingleObject(CompleteEvent, INFINITE);
      CloseHandle(CompleteEvent);
  
      DestroyThreadPool(&ThreadPool);
  }

      我们把工作项目放到一个队列中,用一个信号量通知线程池,线程池中任意一个线程取出工作项目来执行,执行完毕之后,线程返回线程池,继续等待新的工作项目。
      线程池中线程的数量是固定的,预先创建好的,永久的线程,直到销毁线程池的时候,这些线程才会被销毁。
      线程池中线程获得工作项目的机会是均等的,随机的,并没有特别的方式保证哪一个线程具有特殊的优先获得工作项目的机会。
      而且,同一时刻可以并发运行的线程数目没有任何限定。事实上,在我们的执行计算任务的演示代码中,所有的线程都并发执行。
      下面,我们再来看一下,完成同样的任务,系统提供的线程池是如何运作的。
  
   /************************************************************************/
   /* QueueWorkItem Test.                                                  */
   /************************************************************************/
  
  DWORD BeginTime;
  LONG  ItemCount;
  HANDLE CompleteEvent;
  
   int compute()
   {
      srand(BeginTime);
  
      for(int i=0; i<20 *1000 * 1000; i++)
          rand();
  
      return rand();
  }

  
  DWORD WINAPI WorkItem(LPVOID lpParameter)
   {
      BOOL bWaitMode = (BOOL)lpParameter;
  
      if(bWaitMode)
          Sleep(1000);
      else
          compute();
  
      if(InterlockedDecrement(&ItemCount) == 0)
      {
          printf("Time total %d second.\n", GetTickCount() - BeginTime);
          SetEvent(CompleteEvent);
      }

  
      return 0;
  }

  
   void TestWorkItem(BOOL bWaitMode, DWORD Flag)
   {
      CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
      BeginTime = GetTickCount();
      ItemCount = 20;
      
      for(int i=0; i<20; i++)
      {
          QueueUserWorkItem(WorkItem, (PVOID)bWaitMode, Flag);
      }
    
  
      WaitForSingleObject(CompleteEvent, INFINITE);
      CloseHandle(CompleteEvent);
  }
      很简单,是吧?我们仅需要关注于我们的回调函数即可。但是与我们的简单模拟来比,系统提供的线程池有着更多的优点。
      首先,线程池中线程的数目是动态调整的,其次,线程池利用IO完成端口的特性,它可以限制并发运行的线程数目,默认情况下,将会限制为CPU的数目,这可以减少线程切换。它挑选最近执行过的线程再次投入执行,从而避免了不必要的线程切换。
      系统提供的线程池背后的策略,我们下一节继续再谈。
  
  参考书目
  
  1,    MSDN Library 
  2,    《Windows高级编程指南》
  3,    《Windows核心编程》
  4,    《Windows 2000 设备驱动程序设计指南》
目录
相关文章
|
程序员 API 数据库
|
程序员 API 数据库
VC多线程编程
一、问题的提出 编写一个耗时的单线程程序:   新建一个基于对话框的应用程序SingleThread,在主对话框IDD_SINGLETHREAD_DIALOG添加一个按钮,ID为IDC_SLEEP_SIX_SECOND,标题为“延时6秒”,添加按钮的响应函数,代码如下:  void CSingleThreadDlg::OnSleepSixSecond() { Sleep(6000); //延时6秒}   编译并运行应用程序,单击“延时6秒”按钮,你就会发现在这6秒期间程序就象“死机”一样,不在响应其它消息。
949 0
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
48 1
C++ 多线程之初识多线程
|
30天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3
|
30天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
19 2
|
30天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
30 2
|
30天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
34 1
|
30天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
38 1
|
30天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
25 1
|
1月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
48 6