C++ Programmer's Cookbook

{C++ 基础} {C++ 高级} {C#界面,C++核心算法} {设计模式} {C#基础}

windows核心编程--线程高级

暂停和恢复线程的运行

在线程内核对象的内部有一个值,用于指明线程的暂停计数。当调用C r e a t e P r o c e s s或C r e a t e T h r e a d函数时,就创建了线程的内核对象,并且它的暂停计数被初始化为1。当线程的暂停计数是0的时候,除非线程正在等待其他某种事情的发生,否则该线程就处于可调度状态。

恢复线程,可以用将调用R e s u m e T h r e a d
并将C r e a t e T h r e a d函数时返回的线程句柄传递给它(或者是将传递给C r e a t e P r o c e s s的p p i P r o c I n f o参数指向的线程句柄传递给它):

DWORD ResumeThread(HANDLE hThread);
当创建线程时,除了使用C R E AT E _ S U S P E N D E D外,也可以调用S u s p e n dT h r e a d函数来暂停线程的运行:

DWORD SuspendThread(HANDLE hThread);
暂停和恢复进程的运行

对于Wi n d o w s来说,不存在暂停或恢复进程的概念,因为进程从来不会被安排获得C P U时间。Wi n d o w s确实允许一个进程暂停另一个进程中的所有线程的运行,但是从事暂停操作的进程必须是个调试程序。特别是,进程必须调用Wa i t F o r D e b u g E v e n t和C o n t i n u e D e b u g E v e n t之类的函数。
VOID SuspendProcess(DWORD dwProcessID, BOOL fSuspend)
{
   
//Get the list of threads in the system.
   HANDLE hSnapshot = CreateToolhelp32Snapshot(
      TH32CS_SNAPTHREAD, dwProcessID);

   
if(hSnapshot != INVALID_HANDLE_VALUE) 
   
{
      
//Walk the list of threads.
      THREADENTRY32 te = sizeof(te) };
      BOOL fOk 
= Thread32First(hSnapshot, &te);

      
for(; fOk; fOk = Thread32Next(hSnapshot, &te)) 
      
{
         
//Is this thread in the desired process?
         if(te.th32OwnerProcessID == dwProcessID) 
         
{
            
//Attempt to convert the thread ID into a handle.
            HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,
               FALSE, te.th32ThreadID);

            
if(hThread != NULL) 
            
{
               
//Suspend or resume the thread.
               if(fSuspend)
                  SuspendThread(hThread);
               
else
                  ResumeThread(hThread);
            }

            CloseHandle(hThread);
         }

      }

      CloseHandle(hSnapshot);
   }

}
以上是作者实现的,但是有的时候会出错.

 睡眠方式

线程也能告诉系统,它不想在某个时间段内被调度。这是通过调用S l e e p函数来实现的:

VOID Sleep(DWORD dwMilliseconds);
关于S l e e p函数,有下面几个重要问题值得注意:

• 调用S l e e p,可使线程自愿放弃它剩余的时间片。

• 系统将在大约的指定毫秒数内使线程不可调度。不错,如果告诉系统,想睡眠1 0 0 m s,那么可以睡眠大约这么长时间,但是也可能睡眠数秒钟或者数分钟。记住, Wi n d o w s不是个实时操作系统。虽然线程可能在规定的时间被唤醒,但是它能否做到,取决于系统中还有什么操作正在进行。

• 可以调用S l e e p,并且为d w M i l l i s e c o n d s参数传递I N F I N I T E。这将告诉系统永远不要调度该线程。这不是一件值得去做的事情。最好是让线程退出,并还原它的堆栈和内核对象。

• 可以将0传递给S l e e p。这将告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。但是,系统可以对刚刚调用S l e e p的线程重新调度。如果不存在多个拥有相同优先级的可调度线程,就会出现这种情况。

转换到另一个线程

系统提供了一个称为S w i t c h To T h r e a d的函数,使得另一个可调度线程(如果存在能够运行):

BOOL SwitchToThread();

当调用这个函数的时候,系统要查看是否存在一个迫切需要C P U时间的线程。如果没有线程迫切需要C P U时间,S w i t c h To T h r e a d就会立即返回。如果存在一个迫切需要C P U时间的线程,S w i t c h To T h r e a d就对该线程进行调度(该线程的优先级可能低于调用S w i t c h To T h r e a d的线程)。

线程的运行时间

//Get the current time (start time).
DWORD dwStartTime = GetTickCount();

//Perform complex algorithm here.

//Subtract start time from current time to get duration.
DWORD dwElapsedTime = GetTickCount() - dwStartTime;

这个代码做了一个简单的假设:即它不会被中断。但是,在抢占式操作系统中,永远无法知道线程何时被赋予C P U时间。



幸运的是,Wi n d o w s提供了一个称为G e t T h r e a d Ti m e s的函数,它能返回这些信息:

BOOL GetThreadTimes(HANDLE hThread,
   PFILETIME pftCreationTime, PFILETIME pftExitTime,
   PFILETIME pftKernelTime, PFILETIME pftUserTime);

使用实例

__int64 FileTimeToQuadWord(PFILETIME pft) 
{
   
return (Int64ShllMod32(
      pft
->dwHighDateTime, 32| pft->dwLowDateTime);
}


void PerformLongOperation() 
{
   FILETIME ftKernelTimeStart, ftKernelTimeEnd;
   FILETIME ftUserTimeStart,   ftUserTimeEnd;

   FILETIME ftDummy;
   __int64 qwKernelTimeElapsed, qwUserTimeElapsed,
      qwTotalTimeElapsed;

   
//Get starting times.
   GetThreadTimes(GetCurrentThread(), &ftDummy,
      
&ftDummy, &ftKernelTimeStart, &ftUserTimeStart);

   
//Perform complex algorithm here.

   
//Get ending times.
   GetThreadTimes(GetCurrentThread(), &ftDummy,
      
&ftDummy, &ftKernelTimeEnd, &ftUserTimeEnd);

   
//Get the elapsed kernel and user times by 
   
//converting the start and end times 
   
//from FILETIMEs to quad words, and then 
   
//subtract the start times from the end times.

   qwKernelTimeElapsed 
= 
      FileTimeToQuadWord(
&ftKernelTimeEnd) -
      FileTimeToQuadWord(
&ftKernelTimeStart);

   qwUserTimeElapsed 
= 
      FileTimeToQuadWord(
&ftUserTimeEnd) -
      FileTimeToQuadWord(
&ftUserTimeStart);

   
//Get total time duration by adding the kernel
   
//and user times.

   qwTotalTimeElapsed 
= qwKernelTimeElapsed + 
      qwUserTimeElapsed;

   
//The total elapsed time is in 
   
//qwTotalTimeElapsed.
}

注意,G e t P r o c e s s Ti m e s是个类似G e t T h r e a d Ti m e s的函数,适用于进程中的所有线程:

BOOL GetProcessTimes(HANDLE hProcess,
   PFILETIME pftCreationTime, PFILETIME pftExitTime,
   PFILETIME pftKernelTime, PFILETIME pftUserTime);

对于高分辨率的配置文件来说, G e t T h r e a d Ti m e s并不完美。Wi n d o w s确实提供了一些高分辨率性能函数:

BOOL QueryPerformanceFrequency(
   LARGE_INTEGER* pliFrequency);

BOOL QueryPerformanceCounter(
   LARGE_INTEGER* pliCount);

虽然这些函数认为,正在执行的线程并没有得到抢占的机会,但是高分辨率的配置文件是为短期存在的代码块设置的。为了使这些函数运行起来更加容易一些,我创建了下面这个C + +类:

class CStopwatch 
{
public:
   CStopwatch() 
   

      QueryPerformanceFrequency(
&m_liPerfFreq);
      Start();
   }


   
void Start() 
   

      QueryPerformanceCounter(
&m_liPerfStart);
   }


   __int64 Now() 
const 
   
{
      
//Returns # of milliseconds since
      
//Start was called

      LARGE_INTEGER liPerfNow;
      QueryPerformanceCounter(
&liPerfNow);

      
return (((liPerfNow.QuadPart - 
         m_liPerfStart.QuadPart) 
* 1000)/
         m_liPerfFreq.QuadPart);
   }


private:

   
//Counts per second
   LARGE_INTEGER m_liPerfFreq;   

   
//Starting count
   LARGE_INTEGER m_liPerfStart;  
}
;

使用这个类如下:(这样的封装类很多的,我的blog有介绍)

//Create a stopwatch timer
//(which defaults to the current time).
CStopwatch stopwatch;

//Execute the code I want to profile here.

//Get how much time has elapsed up to now.
  __int64 qwElapsedTime = stopwatch.Now();

//qwElapsedTime indicates how long 
//the profiled code executed in milliseconds.

运用结构环境(跟cpu的类型有关系)

环境结构使得系统能够记住线程的状态,这样,当下次线程拥有可以运行的C P U时,它就能够找到它上次中断运行的地方。

Wi n d o w s实际上允许查看线程内核对象的内部情况,以便抓取它当前的一组C P U寄存器。若要进行这项操作,只需要调用G e t T h r e a d C o n t e x t函数:

BOOL GetThreadContext(HANDLE hThread,
   PCONTEXT pContext);

Wi n d o w s使你能够修改C O N T E X T结构中的成员,然后通过调用S e t T h r e a d C o n t e x t将新寄存器值放回线程的内核对象中:

BOOL SetThreadContext(HANDLE hThread,
   CONST CONTEXT *pContext);

线程的优先级

若要设置和获得线程的相对优先级,必须调用下面的这些函数:

BOOL SetThreadPriority(HANDLE hThread,
   int nPriority);

下面是检索线程的相对优先级的补充函数:

int GetThreadPriority(HANDLE hThread);



程序的优先级(进程的优先级)

下面是如何使一个进程将它自己的优先级类设置为空闲的例子:

BOOL SetPriorityClass(GetCurrentProcess(),
   IDLE_PRIORITY_CLASS);

下面是用来检索进程的优先级类的补充函数:

DWORD GetPriorityClass(HANDLE hProcess);



动态提高线程的优先级等级

有些编程人员抱怨说,系统动态提高线程优先级等级的功能对他们的线程性能会产生一种不良的影响,为此M i c r o s o f t增加了下面两个函数,这样就能够使系统的动态提高线程优先级等级的功能不起作用:

BOOL SetProcessPriorityBoost(HANDLE hProcess,
   BOOL DisablePriorityBoost);

BOOL SetThreadPriorityBoost(HANDLE hThread,
   BOOL DisablePriorityBoost);

这两个函数具有许多相似的共性,可以用来确定是激活还是停用优先级提高功能:

BOOL GetProcessPriorityBoost(HANDLE hProcess,
   PBOOL pDisablePriorityBoost);

BOOL GetThreadPriorityBoost(HANDLE hThread,
   PBOOL pDisablePriorityBoost);


为前台进程调整调度程序(windows设置提供可以自定义优先执行application或后台的server)


Windows 2000实际上允许用户对这种调整进行相应的配置。在System Properties(系统属性)对话框的A d v a n c e d选项卡上,用户可以单击Performance Options(性能选项)按钮

posted on 2006-09-14 10:13 梦在天涯 阅读(2240) 评论(0)  编辑 收藏 引用 所属分类: Windows API


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

EMail:itech001#126.com

导航

统计

  • 随笔 - 461
  • 文章 - 4
  • 评论 - 746
  • 引用 - 0

常用链接

随笔分类

随笔档案

收藏夹

Blogs

c#(csharp)

C++(cpp)

Enlish

Forums(bbs)

My self

Often go

Useful Webs

Xml/Uml/html

搜索

  •  

积分与排名

  • 积分 - 1784783
  • 排名 - 5

最新评论

阅读排行榜