??xml version="1.0" encoding="utf-8" standalone="yes"?>国产精品午夜久久,人妻精品久久久久中文字幕69,久久精品国产精品亚洲精品http://www.shnenglu.com/ivenher/articles/15815.html爱饭?/dc:creator>爱饭?/author>Thu, 30 Nov 2006 08:11:00 GMThttp://www.shnenglu.com/ivenher/articles/15815.htmlhttp://www.shnenglu.com/ivenher/comments/15815.htmlhttp://www.shnenglu.com/ivenher/articles/15815.html#Feedback4http://www.shnenglu.com/ivenher/comments/commentRss/15815.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/15815.html
所以学习多U程~程最重要的不是学习API,而是理解什么才是多U程安全的代?br />
从例子说?br />
#include <windows.h>
#include 
<process.h>

long global1 = 0;
volatile long global2 = 0;

class MyClass
{
public:
    MyClass() : m(
0)
    {
        
++m;
    }

    
int fun(int v)
    {
        
return m+v; //-----------9
    }

    
void set(int v)
    {
        m 
= v;   //-------------10
    }
    
int m;
};

MyClass global_object; 
//-------------8

unsigned 
int __stdcall thread_fun1(void *param)
{
    
static int static2 = 0;
    
static MyClass static_object; //--------6
    int local1 = 0;
    
    
++local1;     //-------1
    ++static2;    //-------2
    ++global1;    //-------3
    ++global2;    //-------4
    InterlockedIncrement(&global1); //--------5

    local1 
= global_object.fun(local1); //----------7

    global_object.
set(local1); //---------------11

    
return 0;
}


unsigned 
int __stdcall thread_fun2(void *param)
{
    
++global1;    //-------3
    ++global2;    //-------4
    InterlockedIncrement(&global1); //--------5

    global_object.
set(1); //-----------11
    return 0;
}


int main()
{
    HANDLE thread1 
= (HANDLE)_beginthreadex(0,0,&thread_fun1,0,0,0); //thread 1
    HANDLE thread2 = (HANDLE)_beginthreadex(0,0,&thread_fun1,0,0,0); //thread 2
    HANDLE thread3 = (HANDLE)_beginthreadex(0,0,&thread_fun2,0,0,0); //thread 3
    
    WaitForSingleObject(thread1,INFINITE);
    WaitForSingleObject(thread2,INFINITE);
    WaitForSingleObject(thread3,INFINITE);
    
    
return 0;
}




1.局部变量局部用是安全?br />Z?因ؓ每个thread 都有自己的运行堆栈,而局部变量是生存在堆栈中,大家不干扰?br />所以代?
int local1;
++local1;
是安全的

2.全局原生变量多线E读写是不安全的
全局变量是在?heap)?br />long global1 = 0;
++global2;
++q个操作其实分ؓ两部Q一个是读,另外一个是?br /> mov         ecx,global
 add         ecx,1
 mov         global,ecx
所以代?处是不安全的

3.函数静态变量多U程d也是不安全的
道理?
所以代?处也是不安全?br />
4.volatile能保证全局整Ş变量是多U程安全的么
不能?br />volatile仅仅是告诫compiler不要对这个变量作优化Q每ơ都要从memory取数|而不是从register
所以代?也不是安?br />
5.InterlockedIncrement保证整型变量自增的原子?/strong>
所以代?是安全的

6.function static object的初始化是多U程安全的么
不是?br />著名的Meyer Singleton其实不是U程安全?br />Object & getInstance()

     static Object o;
     return o;
}
可能会造成多次初始化对?br />所以代?处是不安全的

7.?2机器上,4字节整Ş一ơassign是原子的
比如
i =10; //thread1
i=4; //thread2
不会Di的值处于未知状?要么?0要么?


写好多线E安全的法宝是装Q数据有保护的被访问到
安全性:
局部变?gt;成员变量>全局变量

]]>
深入出Win32多线E程序设计之U程控制 ?/title><link>http://www.shnenglu.com/ivenher/articles/15355.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Sat, 18 Nov 2006 06:21:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/15355.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/15355.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/15355.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/15355.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/15355.html</trackback:ping><description><![CDATA[5.讄U程优先U?br /><br />  当一个线E被首次创徏Ӟ它的优先U等同于它所属进E的优先U。在单个q程内可以通过调用SetThreadPriority函数改变U程的相对优先。一个线E的优先U是相对于其所属进E的优先U而言的?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>BOOL SetThreadPriority(HANDLE hThread, int nPriority); </td></tr></tbody></table><br />  其中参数hThread是指向待修改优先U线E的句柄Q线E与包含它的q程的优先关系如下Q?br /><br />   U程优先U?= q程cd本优先 + U程相对优先U?br /><br />  q程cȝ基本优先U包括:<br /><br />  Q?Q实ӞREALTIME_PRIORITY_CLASSQ?br /><br />  Q?Q高QHIGH _PRIORITY_CLASSQ?br /><br />  Q?Q高于正常:ABOVE_NORMAL_PRIORITY_CLASSQ?br /><br />  Q?Q正常:NORMAL _PRIORITY_CLASSQ?br /><br />  Q?Q低于正常:BELOW_ NORMAL _PRIORITY_CLASSQ?br /><br />  Q?Q空ԌIDLE_PRIORITY_CLASS?br /><br />  我们从Win32d理器中可以直观的看到这六个q程cM先Q如下图Q?br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img src="http://dev.yesky.com/imagelist/05/12/7hdedy7210ey.jpg" border="0" /></div></td></tr></tbody></table><br />  U程的相对优先包括Q?br /><br />  Q?Q空ԌTHREAD_PRIORITY_IDLEQ?br /><br />  Q?Q最低线E:THREAD_PRIORITY_LOWESTQ?br /><br />  Q?Q低于正常线E:THREAD_PRIORITY_BELOW_NORMALQ?br /><br />  Q?Q正常线E:THREAD_PRIORITY_ NORMAL (~省)Q?br /><br />  Q?Q高于正常线E:THREAD_PRIORITY_ABOVE_NORMALQ?br /><br />  Q?Q最高线E:THREAD_PRIORITY_HIGHESTQ?br /><br />  Q?Q关键时_THREAD_PRIOTITY_CRITICAL?br /><br />  下图l出了进E优先和线E相对优先的映关p:<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img src="http://dev.yesky.com/imagelist/05/12/62v88f956u3z.jpg" border="0" /></div></td></tr></tbody></table><br />  例如Q?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>HANDLE hCurrentThread = GetCurrentThread();<br />//获得该线E句?br />SetThreadPriority(hCurrentThread, THREAD_PRIORITY_LOWEST); </td></tr></tbody></table><br />  6.睡眠<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>VOID Sleep(DWORD dwMilliseconds);</td></tr></tbody></table><br />  该函数可使线E暂停自qq行Q直到dwMilliseconds毫秒q去为止。它告诉pȝQ自w不惛_某个旉D内被调度?br /><br />  7.其它重要API<br /><br />  获得U程优先U?br /><br />  一个线E被创徏Ӟ׃有一个默认的优先U,但是有时要动态地改变一个线E的优先U,有时需获得一个线E的优先U?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>Int GetThreadPriority (HANDLE hThread);</td></tr></tbody></table><br />  如果函数执行发生错误Q会q回THREAD_PRIORITY_ERROR_RETURN标志。如果函数成功地执行Q会q回优先U标志?br /><br />  获得U程退出码<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>BOOL WINAPI GetExitCodeThread(<br /> HANDLE hThread,<br /> LPDWORD lpExitCode<br />);</td></tr></tbody></table><br />  如果执行成功QGetExitCodeThreadq回TRUEQ退出码被lpExitCode指向内存记录Q否则返回FALSEQ我们可通过GetLastError()L错误原因。如果线E尚未结束,lpExitCode带回来的是STILL_ALIVE?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>获得/讄U程上下?br />BOOL WINAPI GetThreadContext(<br /> HANDLE hThread,<br /> LPCONTEXT lpContext<br />);<br />BOOL WINAPI SetThreadContext(<br /> HANDLE hThread,<br /> CONST CONTEXT *lpContext<br />);</td></tr></tbody></table><br />  ׃GetThreadContext和SetThreadContext可以操作CPU内部的寄存器Q因此在一些高U技巧的~程中有一定应用。譬如,调试器可利用GetThreadContext挂v被调试线E获取其上下文,q设|上下文中的标志寄存器中的陷阱标志位Q最后通过SetThreadContext使设|生效来q行单步调试?br /><br />  8.实例<br /><br />  以下E序使用CreateThread创徏两个U程Q在q两个线E中Sleep一D|_ȝE通过GetExitCodeThread来判断两个线E是否结束运行:<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>#define WIN32_LEAN_AND_MEAN<br />#include <stdio.h><br />#include <stdlib.h><br />#include <windows.h><br />#include <conio.h><br /><br />DWORD WINAPI ThreadFunc(LPVOID);<br /><br />int main()<br />{<br /> HANDLE hThrd1;<br /> HANDLE hThrd2;<br /> DWORD exitCode1 = 0;<br /> DWORD exitCode2 = 0;<br /> DWORD threadId;<br /><br /> hThrd1 = CreateThread(NULL, 0, ThreadFunc, (LPVOID)1, 0, &threadId );<br /> if (hThrd1)<br />  printf("Thread 1 launched\n");<br /><br /> hThrd2 = CreateThread(NULL, 0, ThreadFunc, (LPVOID)2, 0, &threadId );<br /> if (hThrd2)<br />  printf("Thread 2 launched\n");<br /><br /> // Keep waiting until both calls to GetExitCodeThread succeed AND<br /> // neither of them returns STILL_ACTIVE.<br /> for (;;)<br /> {<br />  printf("Press any key to exit..\n");<br />  getch();<br /><br />  GetExitCodeThread(hThrd1, &exitCode1);<br />  GetExitCodeThread(hThrd2, &exitCode2);<br />  if ( exitCode1 == STILL_ACTIVE )<br />   puts("Thread 1 is still running!");<br />  if ( exitCode2 == STILL_ACTIVE )<br />   puts("Thread 2 is still running!");<br />  if ( exitCode1 != STILL_ACTIVE && exitCode2 != STILL_ACTIVE )<br />   break;<br /> }<br /><br /> CloseHandle(hThrd1);<br /> CloseHandle(hThrd2);<br /><br /> printf("Thread 1 returned %d\n", exitCode1);<br /> printf("Thread 2 returned %d\n", exitCode2);<br /><br /> return EXIT_SUCCESS;<br />}<br /><br />/*<br />* Take the startup value, do some simple math on it,<br />* and return the calculated value.<br />*/<br />DWORD WINAPI ThreadFunc(LPVOID n)<br />{<br /> Sleep((DWORD)n*1000*2);<br /> return (DWORD)n * 10;<br />}</td></tr></tbody></table><br />  通过下面的程序我们可以看出多U程E序q行序的难以预料以及WINAPI的CreateThread函数与Cq行时库的_beginthread的差别:<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>#define WIN32_LEAN_AND_MEAN<br />#include <stdio.h><br />#include <stdlib.h><br />#include <windows.h><br /><br />DWORD WINAPI ThreadFunc(LPVOID);<br /><br />int main()<br />{<br /> HANDLE hThrd;<br /> DWORD threadId;<br /> int i;<br /><br /> for (i = 0; i < 5; i++)<br /> {<br />  hThrd = CreateThread(NULL, 0, ThreadFunc, (LPVOID)i, 0, &threadId);<br />  if (hThrd)<br />  {<br />   printf("Thread launched %d\n", i);<br />   CloseHandle(hThrd);<br />  }<br /> }<br /> // Wait for the threads to complete.<br /> Sleep(2000);<br /><br /> return EXIT_SUCCESS;<br />}<br /><br />DWORD WINAPI ThreadFunc(LPVOID n)<br />{<br /> int i;<br /> for (i = 0; i < 10; i++)<br />  printf("%d%d%d%d%d%d%d%d\n", n, n, n, n, n, n, n, n);<br /> return 0;<br />}</td></tr></tbody></table><br />  q行的输出具有很大的随机性,q里摘取了几ơ结果的一部分Q几乎每一ơ都不同Q:<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img src="http://dev.yesky.com/imagelist/05/12/2269bdvd5va1.jpg" border="0" /><img src="http://dev.yesky.com/imagelist/05/12/94hfp586n83a.jpg" border="0" /><img src="http://dev.yesky.com/imagelist/05/12/phqe9k2g8p2f.jpg" border="0" /></div></td></tr></tbody></table><br />  如果我们使用标准C库函数而不是多U程版的q行时库Q则E序可能输出"3333444444"q样的结果,而用多U程q行时库后,则可避免q一问题?br /><br />  下列E序在主U程中创Z个SecondThreadQ在SecondThreadU程中通过自增对Counter计数?000000Q主U程一直等待其l束Q?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>#include <Win32.h><br />#include <stdio.h><br />#include <process.h><br /><br />unsigned Counter;<br />unsigned __stdcall SecondThreadFunc(void *pArguments)<br />{<br /> printf("In second thread...\n");<br /><br /> while (Counter < 1000000)<br />  Counter++;<br /><br /> _endthreadex(0);<br /> return 0;<br />}<br /><br />int main()<br />{<br /> HANDLE hThread;<br /> unsigned threadID;<br /><br /> printf("Creating second thread...\n");<br /><br /> // Create the second thread.<br /> hThread = (HANDLE)_beginthreadex(NULL, 0, &SecondThreadFunc, NULL, 0, &threadID);<br /><br /> // Wait until second thread terminates <br /> WaitForSingleObject(hThread, INFINITE);<br /> printf("Counter should be 1000000; it is-> %d\n", Counter);<br /> // Destroy the thread object.<br /> CloseHandle(hThread);<br />}<br /></td></tr></tbody></table><div id="ljb55lx" class="right"> </div><img src ="http://www.shnenglu.com/ivenher/aggbug/15355.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-11-18 14:21 <a href="http://www.shnenglu.com/ivenher/articles/15355.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入出Win32多线E程序设计之U程控制 一http://www.shnenglu.com/ivenher/articles/15353.html爱饭?/dc:creator>爱饭?/author>Sat, 18 Nov 2006 06:20:00 GMThttp://www.shnenglu.com/ivenher/articles/15353.htmlhttp://www.shnenglu.com/ivenher/comments/15353.htmlhttp://www.shnenglu.com/ivenher/articles/15353.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/15353.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/15353.html
  WIN32U程控制主要实现U程的创建、终止、挂起和恢复{操作,q些操作都依赖于WIN32提供的一lAPI和具体编译器的Cq行时库函数?br />
  1.U程函数

  在启动一个线E之前,必须为线E编写一个全局的线E函敎ͼq个U程函数接受一?2位的LPVOID作ؓ参数Q返回一个UINTQ线E函数的l构为:

UINT ThreadFunction(LPVOID pParam)
{
 //U程处理代码
 return0;
}

  在线E处理代码部分通常包括一个死循环Q该循环中先{待某事情的发生Q再处理相关的工作:

while(1)
{
 WaitForSingleObject(??;//或WaitForMultipleObjects(?
 //Do something
}

  一般来_C++的类成员函数不能作ؓU程函数。这是因为在cM定义的成员函敎ͼ~译器会l其加上this指针。请看下列程序:

#include "windows.h"
#include <process.h>
class ExampleTask
{
 public:
  void taskmain(LPVOID param);
  void StartTask();
};
void ExampleTask::taskmain(LPVOID param)
{}

void ExampleTask::StartTask()
{
 _beginthread(taskmain,0,NULL);
}

int main(int argc, char* argv[])
{
 ExampleTask realTimeTask;
 realTimeTask.StartTask();
 return 0;
}

  E序~译时出现如下错误:

error C2664: '_beginthread' : cannot convert parameter 1 from 'void (void *)' to 'void (__cdecl *)(void *)'
None of the functions with this name in scope match the target type

  再看下列E序Q?br />
#include "windows.h"
#include <process.h>
class ExampleTask
{
 public:
  void taskmain(LPVOID param);
};

void ExampleTask::taskmain(LPVOID param)
{}

int main(int argc, char* argv[])
{
 ExampleTask realTimeTask;
 _beginthread(ExampleTask::taskmain,0,NULL);
 return 0;
}

  E序~译时会出错Q?br />
error C2664: '_beginthread' : cannot convert parameter 1 from 'void (void *)' to 'void (__cdecl *)(void *)'
None of the functions with this name in scope match the target type

  如果一定要以类成员函数作ؓU程函数Q通常有如下解x案:

  Q?Q将该成员函数声明ؓstaticcdQ去掉this指针Q?br />
  我们上qC个程序改变ؓQ?br />
#include "windows.h"
#include <process.h>
class ExampleTask
{
 public:
  void static taskmain(LPVOID param);
  void StartTask();
};

void ExampleTask::taskmain(LPVOID param)
{}

void ExampleTask::StartTask()
{
 _beginthread(taskmain,0,NULL);
}

int main(int argc, char* argv[])
{
 ExampleTask realTimeTask;
 realTimeTask.StartTask();
 return 0;
}
?br />#include "windows.h"
#include <process.h>
class ExampleTask
{
 public:
  void static taskmain(LPVOID param);
};

void ExampleTask::taskmain(LPVOID param)
{}

int main(int argc, char* argv[])
{
 _beginthread(ExampleTask::taskmain,0,NULL);
 return 0;
}

  均编译通过?br />
  成员函数声明ؓ静态虽然可以解决作为线E函数的问题Q但是它带来了新的问题,那就是static成员函数只能讉Kstatic成员。解x问题的一U途径是可以在调用c静态成员函敎ͼU程函数Q时this指针作ؓ参数传入Qƈ在改U程函数中用强制cd转换this转换成指向该cȝ指针Q通过该指针访问非静态成员?br />
  Q?Q不定义cL员函CؓU程函数Q而将U程函数定义为类的友元函数。这PU程函数也可以有cL员函数同{的权限Q?

  我们程序修改ؓQ?br />
#include "windows.h"
#include <process.h>
class ExampleTask
{
 public:
  friend void taskmain(LPVOID param);
  void StartTask();
};

void taskmain(LPVOID param)
{
 ExampleTask * pTaskMain = (ExampleTask *) param;
 //通过pTaskMain指针引用
}

void ExampleTask::StartTask()
{
 _beginthread(taskmain,0,this);
}
int main(int argc, char* argv[])
{
 ExampleTask realTimeTask;
 realTimeTask.StartTask();
 return 0;
}

  Q?Q可以对非静态成员函数实现回调,q访问非静态成员,此法涉及C些高U技巧,在此不再详述?br />

]]>
深入出Win32多线E程序设计之U程控制 ?/title><link>http://www.shnenglu.com/ivenher/articles/15354.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Sat, 18 Nov 2006 06:20:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/15354.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/15354.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/15354.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/15354.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/15354.html</trackback:ping><description><![CDATA[2.创徏U程<br /><br />  q程的主U程由操作系l自动生成,Win32提供了CreateThread API来完成用LE的创徏Q该API的原型ؓQ?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>HANDLE CreateThread(<br /> LPSECURITY_ATTRIBUTES lpThreadAttributes,//Pointer to a SECURITY_ATTRIBUTES structure<br /> SIZE_T dwStackSize, //Initial size of the stack, in bytes.<br /> LPTHREAD_START_ROUTINE lpStartAddress,<br /> LPVOID lpParameter, //Pointer to a variable to be passed to the thread<br /> DWORD dwCreationFlags, //Flags that control the creation of the thread<br /> LPDWORD lpThreadId //Pointer to a variable that receives the thread identifier<br />);</td></tr></tbody></table><br />  如果使用C/C++语言~写多线E应用程序,一定不能用操作系l提供的CreateThread APIQ而应该用C/C++q行时库中的_beginthreadQ或_beginthreadexQ,其函数原型ؓQ?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>uintptr_t _beginthread( <br /> void( __cdecl *start_address )( void * ), //Start address of routine that begins execution of new thread<br /> unsigned stack_size, //Stack size for new thread or 0.<br /> void *arglist //Argument list to be passed to new thread or NULL<br />);<br />uintptr_t _beginthreadex( <br /> void *security,//Pointer to a SECURITY_ATTRIBUTES structure<br /> unsigned stack_size,<br /> unsigned ( __stdcall *start_address )( void * ),<br /> void *arglist,<br /> unsigned initflag,//Initial state of new thread (0 for running or CREATE_SUSPENDED for suspended); <br /> unsigned *thrdaddr <br />);</td></tr></tbody></table><br />  _beginthread函数与Win32 API 中的CreateThread函数cMQ但有如下差异: <br /><br />  Q?Q通过_beginthread函数我们可以利用其参数列表arglist多个参C递到U程Q?<br /><br />  Q?Q_beginthread 函数初始化某?C q行时库变量Q在U程中若需要?C q行时库?<br /><br />  3.l止U程<br /><br />  U程的终止有如下四种方式Q?br /><br />  Q?Q线E函数返回;<br /><br />  Q?Q线E自w调用ExitThread 函数即终止自己,其原型ؓQ?br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>VOID ExitThread(UINT fuExitCode ); </td></tr></tbody></table><br />  它将参数fuExitCode讄为线E的退出码?br /><br />  注意Q如果用C/C++~写代码Q我们应该用C/C++q行时库函数_endthread (_endthreadex)l止U程Q决不能使用ExitThreadQ?br />_endthread 函数对于U程内的条gl止很有用。例如,专门用于通信处理的线E若无法获取寚w信端口的控Ӟ则会退出?br /><br />  Q?Q同一q程或其他进E的U程调用TerminateThread函数Q其原型为:<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode); </td></tr></tbody></table><br />  该函数用来结束由hThread参数指定的线E,q把dwExitCode设成该线E的退出码。当某个U程不再响应Ӟ我们可以用其他线E调用该函数来终止这个不响应的线E?br /><br />  Q?Q包含线E的q程l止?br /><br />  最好用第1U方式终止线E,W?~4U方式都不宜采用?br /><br />  4.挂v与恢复线E?br /><br />  当我们创建线E的时候,如果l其传入CREATE_SUSPENDED标志Q则该线E创建后被挂P我们应用ResumeThread恢复它:<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>DWORD ResumeThread(HANDLE hThread); </td></tr></tbody></table><br />  如果ResumeThread函数q行成功Q它返回线E的前一个暂停计敎ͼ否则q回0x FFFFFFFF?br /><br />  对于没有被挂LU程Q程序员可以调用SuspendThread函数挂v之:<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>DWORD SuspendThread(HANDLE hThread);</td></tr></tbody></table><br />  一个线E可以被挂v多次。线E可以自行暂停运行,但是不能自行恢复q行。如果一个线E被挂vnơ,则该U程也必被恢复nơ才可能得以执行?br /><img src ="http://www.shnenglu.com/ivenher/aggbug/15354.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-11-18 14:20 <a href="http://www.shnenglu.com/ivenher/articles/15354.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32多线E程序设计之U程通信 Q三Q?/title><link>http://www.shnenglu.com/ivenher/articles/2297.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Fri, 30 Dec 2005 10:51:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/2297.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/2297.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/2297.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/2297.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/2297.html</trackback:ping><description><![CDATA[<STRONG>信号?BR><BR></STRONG>  信号量是l护0到指定最大g间的同步对象。信号量状态在其计数大?时是有信LQ而其计数?时是无信L。信号量对象在控制上可以支持有限数量׃n资源的访问?BR><BR>  信号量的特点和用途可用下列几句话定义Q?BR><BR>  Q?Q如果当前资源的数量大于0Q则信号量有效;<BR><BR>  Q?Q如果当前资源数量是0Q则信号量无效;<BR><BR>  Q?Q系l决不允许当前资源的数量|<BR><BR>  Q?Q当前资源数量决不能大于最大资源数量?BR><BR>  创徏信号?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE CreateSemaphore (<BR> PSECURITY_ATTRIBUTE psa,<BR> LONG lInitialCount, //开始时可供使用的资源数<BR> LONG lMaximumCount, //最大资源数<BR>PCTSTR pszName);</TD></TR></TBODY></TABLE><BR>  释放信号?BR><BR>  通过调用ReleaseSemaphore函数Q线E就能够对信标的当前资源数量q行递增Q该函数原型为:<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>BOOL WINAPI ReleaseSemaphore(<BR> HANDLE hSemaphore,<BR> LONG lReleaseCount, //信号量的当前资源数增加lReleaseCount<BR> LPLONG lpPreviousCount<BR>);</TD></TR></TBODY></TABLE><BR>  打开信号?BR><BR>  和其他核心对象一P信号量也可以通过名字跨进E访问,打开信号量的API为:<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE OpenSemaphore (<BR> DWORD fdw<A target=_blank><FONT color=#3366cc>Access</FONT></A>,<BR> BOOL bInherithandle,<BR> PCTSTR pszName<BR>);</TD></TR></TBODY></TABLE><BR>  <B>互锁讉K</B><BR><BR>  当必M原子操作方式来修改单个值时Q互锁访问函数是相当有用的。所谓原子访问,是指U程在访问资源时能够保所有其他线E都不在同一旉内访问相同的资源?BR><BR>  L下列代码Q?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>int globalVar = 0;<BR><BR>DWORD WINAPI ThreadFunc1(LPVOID n)<BR>{<BR> globalVar++;<BR> return 0;<BR>}<BR>DWORD WINAPI ThreadFunc2(LPVOID n)<BR>{<BR> globalVar++;<BR> return 0;<BR>}</TD></TR></TBODY></TABLE><BR>  q行ThreadFunc1和ThreadFunc2U程Q结果是不可预料的,因ؓglobalVar++q不对应着一条机器指令,我们看看globalVar++的反<A target=_blank><FONT color=#3366cc>汇编</FONT></A>代码Q?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>00401038 mov eax,[globalVar (0042d3f0)]<BR>0040103D add eax,1<BR>00401040 mov [globalVar (0042d3f0)],eax</TD></TR></TBODY></TABLE><BR>  ?mov eax,[globalVar (0042d3f0)]" 指o?add eax,1" 指o以及"add eax,1" 指o?mov [globalVar (0042d3f0)],eax"指o之间都可能发生线E切换,使得E序的执行后globalVar的结果不能确定。我们可以用Interlocked<A target=_blank><FONT color=#3366cc>Exchange</FONT></A>Add函数解决q个问题Q?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>int globalVar = 0;<BR><BR>DWORD WINAPI ThreadFunc1(LPVOID n)<BR>{<BR> InterlockedExchangeAdd(&globalVar,1);<BR> return 0;<BR>}<BR>DWORD WINAPI ThreadFunc2(LPVOID n)<BR>{<BR> InterlockedExchangeAdd(&globalVar,1);<BR> return 0;<BR>}</TD></TR></TBODY></TABLE><BR>  InterlockedExchangeAdd保证对变量globalVar的访问具?原子?。互锁访问的控制速度非常快,调用一个互锁函数的CPU周期通常于50Q不需要进行用h式与内核方式的切换(该切换通常需要运?000个CPU周期Q?BR><BR>  互锁讉K函数的缺点在于其只能对单一变量q行原子讉KQ如果要讉K的资源比较复杂,仍要使用临界区或互斥?BR><BR>  可等待定时器<BR><BR>  可等待定时器是在某个旉或按规定的间隔时间发q信号通知的内核对象。它们通常用来在某个时间执行某个操作?BR><BR>  创徏可等待定时器<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE CreateWaitableTimer(<BR> PSECURITY_ATTRISUTES psa,<BR> BOOL fManualReset,//人工重置或自动重|定时器<BR>PCTSTR pszName);</TD></TR></TBODY></TABLE><BR>  讄可等待定时器<BR><BR>  可等待定时器对象在非Ȁzȝ态下被创建,E序员应调用 SetWaitableTimer函数来界定定时器在何时被Ȁz:<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>BOOL SetWaitableTimer(<BR> HANDLE hTimer, //要设|的定时?BR> const LARGE_INTEGER *pDueTime, //指明定时器第一ơ激zȝ旉<BR> LONG lPeriod, //指明此后定时器应该间隔多长时间激zM?BR> PTIMERAPCROUTINE pfnCompletionRoutine,<BR> PVOID PvArgToCompletionRoutine,<BR>BOOL fResume);</TD></TR></TBODY></TABLE><BR>  取消可等待定时器<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>BOOl Cancel WaitableTimer(<BR> HANDLE hTimer //要取消的定时?BR>);</TD></TR></TBODY></TABLE><BR>  打开可等待定时器<BR><BR>  作ؓ一U内核对象,WaitableTimer也可以被其他q程以名字打开Q?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE OpenWaitableTimer (<BR> DWORD fdwAccess,<BR> BOOL bInherithandle,<BR> PCTSTR pszName<BR>);</TD></TR></TBODY></TABLE><BR>  <B>实例</B><BR><BR>  下面l出的一个程序可能发生死锁现象:<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>#include <<A target=_blank><FONT color=#3366cc>Windows</FONT></A>.h><BR>#include <stdio.h><BR>CRITICAL_SECTION cs1, cs2;<BR>long WINAPI ThreadFn(long);<BR>main()<BR>{<BR> long iThreadID;<BR> InitializeCriticalSection(&cs1);<BR> InitializeCriticalSection(&cs2);<BR> CloseHandle(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFn, NULL, 0,&iThreadID));<BR> while (TRUE)<BR> {<BR>  EnterCriticalSection(&cs1);<BR>  printf("\nU程1占用临界?");<BR>  EnterCriticalSection(&cs2);<BR>  printf("\nU程1占用临界?");<BR><BR>  printf("\nU程1占用两个临界?);<BR><BR>  LeaveCriticalSection(&cs2);<BR>  LeaveCriticalSection(&cs1);<BR><BR>  printf("\nU程1释放两个临界?);<BR>  Sleep(20);<BR> };<BR> return (0);<BR>}<BR><BR>long WINAPI ThreadFn(long lParam)<BR>{<BR> while (TRUE)<BR> {<BR>  EnterCriticalSection(&cs2);<BR>  printf("\nU程2占用临界?");<BR>  EnterCriticalSection(&cs1);<BR>  printf("\nU程2占用临界?");<BR><BR>  printf("\nU程2占用两个临界?);<BR><BR>  LeaveCriticalSection(&cs1);<BR>  LeaveCriticalSection(&cs2);<BR><BR>  printf("\nU程2释放两个临界?);<BR>  Sleep(20);<BR> };<BR>}</TD></TR></TBODY></TABLE><BR>  q行q个E序Q在中途一旦发生这L输出Q?BR><BR>  U程1占用临界?<BR><BR>  U程2占用临界?<BR><BR>  ?BR><BR>  U程2占用临界?<BR><BR>  U程1占用临界?<BR><BR>  ?BR><BR>  U程1占用临界?<BR><BR>  U程2占用临界?<BR><BR>  ?BR><BR>  U程2占用临界?<BR><BR>  U程1占用临界?<BR><BR>  E序??掉了Q再也运行不下去。因L输出Q意味着两个U程怺{待Ҏ释放临界区,也即出现了死锁?BR><BR>  如果我们线E?的控制函数改为:<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>long WINAPI ThreadFn(long lParam)<BR>{<BR> while (TRUE)<BR> {<BR>  EnterCriticalSection(&cs1);<BR>  printf("\nU程2占用临界?");<BR>  EnterCriticalSection(&cs2);<BR>  printf("\nU程2占用临界?");<BR><BR>  printf("\nU程2占用两个临界?);<BR><BR>  LeaveCriticalSection(&cs1);<BR>  LeaveCriticalSection(&cs2);<BR><BR>  printf("\nU程2释放两个临界?);<BR>  Sleep(20);<BR> };<BR>}</TD></TR></TBODY></TABLE><BR>  再次q行E序Q死锁被消除Q程序不再挡掉。这是因为我们改变了U程2中获得界区1?的顺序,消除了线E??怺{待资源的可能性?BR><BR>  由此我们得出l论Q在使用U程间的同步机制Ӟ要特别留心死锁的发生?BR><img src ="http://www.shnenglu.com/ivenher/aggbug/2297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2005-12-30 18:51 <a href="http://www.shnenglu.com/ivenher/articles/2297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32多线E程序设计之U程通信 Q二Q?/title><link>http://www.shnenglu.com/ivenher/articles/2296.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Fri, 30 Dec 2005 10:50:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/2296.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/2296.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/2296.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/2296.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/2296.html</trackback:ping><description><![CDATA[<STRONG>临界?BR><BR></STRONG>  定义临界区变?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>CRITICAL_SECTION gCriticalSection;</TD></TR></TBODY></TABLE><BR>  通常情况下,CRITICAL_SECTIONl构体应该被定义为全局变量Q以便于q程中的所有线E方便地按照变量名来引用该结构体?BR><BR>  初始化界区<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>VOID WINAPI InitializeCriticalSection(<BR> LPCRITICAL_SECTION lpCriticalSection<BR> //指向E序员定义的CRITICAL_SECTION变量<BR>);</TD></TR></TBODY></TABLE><BR>  该函数用于对pcs所指的CRITICAL_SECTIONl构体进行初始化。该函数只是讄了一些成员变量,它的q行一般不会失败,因此它采用了VOIDcd的返回倹{该函数必须在Q何线E调用EnterCriticalSection函数之前被调用,如果一个线E试图进入一个未初始化的CRTICAL_SECTIONQ那么结果将是很N计的?BR><BR>  删除临界?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>VOID WINAPI DeleteCriticalSection(<BR> LPCRITICAL_SECTION lpCriticalSection<BR> //指向一个不再需要的CRITICAL_SECTION变量<BR>);</TD></TR></TBODY></TABLE><BR>  q入临界?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>VOID WINAPI EnterCriticalSection(<BR> LPCRITICAL_SECTION lpCriticalSection<BR> //指向一个你卛_锁定的CRITICAL_SECTION变量<BR>);</TD></TR></TBODY></TABLE><BR>  d临界?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>VOID WINAPI LeaveCriticalSection(<BR> LPCRITICAL_SECTION lpCriticalSection<BR> //指向一个你卛_d的CRITICAL_SECTION变量<BR>);</TD></TR></TBODY></TABLE><BR>  使用临界区编E的一般方法是Q?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>void UpdateData()<BR>{<BR> EnterCriticalSection(&gCriticalSection);<BR> ...//do something<BR> LeaveCriticalSection(&gCriticalSection);<BR>}</TD></TR></TBODY></TABLE><BR>  关于临界区的使用Q有下列注意点:<BR><BR>  Q?Q每个共享资源用一个CRITICAL_SECTION变量Q?BR><BR>  Q?Q不要长旉q行关键代码D,当一个关键代码段长时间运行时Q其他线E就会进入等待状态,q会降低应用E序的运行性能Q?BR><BR>  Q?Q如果需要同时访问多个资源,则可能连l调用EnterCriticalSectionQ?BR><BR>  Q?QCritical Section不是OS核心对象Q如果进入界区的线E??了,无法释放界资源。这个缺点在Mutex中得C弥补?BR><BR>  <B>互斥</B><BR><BR>  互斥量的作用是保证每ơ只能有一个线E获得互斥量而得以l执行,使用CreateMutex函数创徏Q?<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE CreateMutex(<BR> LPSECURITY_ATTRIBUTES lpMutexAttributes,<BR> // 安全属性结构指针,可ؓNULL<BR> BOOL bInitialOwner, <BR> //是否占有该互斥量QTRUEQ占有,FALSEQ不占有<BR> LPCTSTR lpName <BR> //信号量的名称<BR>);<BR></TD></TR></TBODY></TABLE><BR>  Mutex是核心对象,可以跨进E访问,下面的代码给Z从另一q程讉K命名Mutex的例子:<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE hMutex;<BR>hMutex = OpenMutex(MUTEX_ALL_<A target=_blank><FONT color=#3366cc>Access</FONT></A>, FALSE, L"mutexName"); <BR>if (hMutex){<BR> ?<BR>?BR>else{<BR> ?BR>}</TD></TR></TBODY></TABLE><BR>  相关APIQ?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>BOOL WINAPI ReleaseMutex(<BR> HANDLE hMutex<BR>);</TD></TR></TBODY></TABLE><BR>  使用互斥~程的一般方法是Q?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>void UpdateResource()<BR>{<BR> WaitForSingleObject(hMutex,?;<BR> ...//do something<BR> ReleaseMutex(hMutex);<BR>}</TD></TR></TBODY></TABLE><BR>  互斥(mutex)内核对象能够保U程拥有对单个资源的互斥讉K权。互斥对象的行ؓҎ与临界区相同,但是互斥对象属于内核对象Q而界区则属于用h式对象,因此q导致mutex与Critical Section的如下不同:<BR><BR>  Q?Q?互斥对象的运行速度比关键代码段要慢Q?BR><BR>  Q?Q?不同q程中的多个U程能够讉K单个互斥对象Q?BR><BR>  Q?Q?U程在等待访问资源时可以讑֮一个超时倹{?BR><BR>  下图更详l地列出了互斥与临界区的不同Q?BR><BR> <TABLE width="90%" align=center border=0> <TBODY> <TR> <TD> <DIV align=center><IMG src="http://image.21tx.com/image/20051216/17850.jpg" border=0></DIV></TD></TR></TBODY></TABLE><BR><img src ="http://www.shnenglu.com/ivenher/aggbug/2296.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2005-12-30 18:50 <a href="http://www.shnenglu.com/ivenher/articles/2296.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32多线E程序设计之U程通信 Q一Q?/title><link>http://www.shnenglu.com/ivenher/articles/2295.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Fri, 30 Dec 2005 10:49:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/2295.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/2295.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/2295.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/2295.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/2295.html</trackback:ping><description><![CDATA[<STRONG>?BR><BR></STRONG>  U程之间<A target=_blank><FONT color=#3366cc>通信</FONT></A>的两个基本问题是互斥和同步?BR><BR>  U程同步是指U程之间所h的一U制U关p,一个线E的执行依赖另一个线E的消息Q当它没有得到另一个线E的消息时应{待Q直到消息到达时才被唤醒?BR><BR>  U程互斥是指对于׃n?A target=_blank><FONT color=#3366cc>操作pȝ</FONT></A>资源Q指的是q义?资源"Q而不?A target=_blank><FONT color=#3366cc>Windows</FONT></A>?res文gQ譬如全局变量是一U共享资源)Q在各线E访问时的排它性。当有若q个U程都要使用某一׃n资源ӞM时刻最多只允许一个线E去使用Q其它要使用该资源的U程必须{待Q直到占用资源者释放该资源?BR><BR>  U程互斥是一U特D的U程同步?BR><BR>  实际上,互斥和同步对应着U程间通信发生的两U情况:<BR><BR>  Q?Q当有多个线E访问共享资源而不使资源被破坏Ӟ<BR><BR>  Q?Q当一个线E需要将某个d已经完成的情况通知另外一个或多个U程时?BR><BR>  在WIN32中,同步机制主要有以下几U:<BR><BR>  Q?Q事?Event);<BR><BR>  Q?Q信号量(semaphore);<BR><BR>  Q?Q互斥量(mutex);<BR><BR>  Q?Q界区(Critical section)?BR><BR>  <B>全局变量</B><BR><BR>  因ؓq程中的所有线E均可以讉K所有的全局变量Q因而全局变量成ؓWin32多线E通信的最单方式。例如:<BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>int var; //全局变量<BR>UINT ThreadFunction(LPVOIDpParam)<BR>{<BR> var = 0;<BR> while (var < MaxValue)<BR> {<BR>  //U程处理<BR>  ::InterlockedIncrement(long*) &var);<BR> }<BR> return 0;<BR>}<BR>L下列E序Q?BR>int globalFlag = false; <BR>DWORD WINAPI ThreadFunc(LPVOID n)<BR>{<BR> Sleep(2000);<BR> globalFlag = true;<BR><BR> return 0;<BR>}<BR><BR>int main()<BR>{<BR> HANDLE hThrd;<BR> DWORD threadId;<BR><BR> hThrd = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &threadId);<BR> if (hThrd)<BR> {<BR>  printf("Thread launched\n");<BR>  CloseHandle(hThrd);<BR> }<BR><BR> while (!globalFlag)<BR> ;<BR> printf("exit\n");<BR>}</TD></TR></TBODY></TABLE><BR>  上述E序中用全局变量和while循环查询q行U程间同步,实际上,q是一U应该避免的ҎQ因为: <BR><BR>  Q?Q当ȝE必M自己与ThreadFunc函数的完成运行实现同步时Q它q没有自己q入睡眠状态。由于主U程没有q入睡眠状态,因此操作pȝl箋为它调度C P U旉Q这p占用其他U程的宝贉|间周期;<BR><BR>  Q?Q当ȝE的优先U高于执行ThreadFunc函数的线E时Q就会发生globalFlag永远不能被赋gؓtrue的情c因为在q种情况下,pȝ决不会将M旉片分配给ThreadFuncU程?BR><BR>  <B>事g</B><BR><BR>  事g(Event)是WIN32提供的最灉|的线E间同步方式Q事件可以处于激发状?signaled or true)或未Ȁ发状?unsignal or false)。根据状态变q方式的不同Q事件可分ؓ两类Q?BR><BR>  Q?Q手动设|:q种对象只可能用E序手动讄Q在需要该事g或者事件发生时Q采用SetEvent及ResetEvent来进行设|?BR><BR>  Q?Q自动恢复:一旦事件发生ƈ被处理后Q自动恢复到没有事g状态,不需要再ơ设|?BR><BR>  创徏事g的函数原型ؓQ?BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE CreateEvent(<BR> LPSECURITY_ATTRIBUTES lpEventAttributes,<BR> // SECURITY_ATTRIBUTESl构指针Q可为NULL<BR> BOOL bManualReset, <BR> // 手动/自动<BR> // TRUEQ在WaitForSingleObject后必L动调用ResetEvent清除信号<BR> // FALSEQ在WaitForSingleObject后,pȝ自动清除事g信号<BR> BOOL bInitialState, //初始状?BR> LPCTSTR lpName //事g的名U?BR>);</TD></TR></TBODY></TABLE><BR>  使用"事g"机制应注意以下事:<BR><BR>  Q?Q如果跨q程讉K事gQ必d事g命名Q在对事件命名的时候,要注意不要与pȝ命名I间中的其它全局命名对象冲突Q?BR><BR>  Q?Q事件是否要自动恢复Q?BR><BR>  Q?Q事件的初始状态设|?BR><BR>  ׃event对象属于内核对象Q故q程B可以调用OpenEvent函数通过对象的名字获得进EA中event对象的句柄,然后这个句柄用于ResetEvent、SetEvent和WaitForMultipleObjects{函C。此法可以实C个进E的U程控制另一q程中线E的q行Q例如: <BR><BR> <TABLE class=txcode cellSpacing=0 cellPadding=0 align=center border=0> <TBODY> <TR> <TD>HANDLE hEvent=OpenEvent(EVENT_ALL_<A target=_blank><FONT color=#3366cc>Access</FONT></A>,true,"MyEvent"); <BR>ResetEvent(hEvent);</TD></TR></TBODY></TABLE><BR><img src ="http://www.shnenglu.com/ivenher/aggbug/2295.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2005-12-30 18:49 <a href="http://www.shnenglu.com/ivenher/articles/2295.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows多线E多d设计初步http://www.shnenglu.com/ivenher/articles/983.html爱饭?/dc:creator>爱饭?/author>Tue, 08 Nov 2005 11:13:00 GMThttp://www.shnenglu.com/ivenher/articles/983.htmlhttp://www.shnenglu.com/ivenher/comments/983.htmlhttp://www.shnenglu.com/ivenher/articles/983.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/983.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/983.html
   QdaQ]当前行的Windows操作pȝQ它能同时运行几个程?独立q行的程序又UCE?Q对于同一个程序,它又可以分成若干个独立的执行,我们UC为线E,U程提供了多d处理的能力。用q程和线E的观点来研IY件是当今普遍采用的方法,q程和线E的概念的出玎ͼҎ高Y件的q行性有着重要的意义。现在的应用软g无一不是多线E多d处理Q单U城的Y件是不可惌的。因此掌握多U程多Q务设计方法对每个E序员都是必需要掌握的。本文针对多U程技术在应用中经帔R到的问题Q如U程间的通信、同步等Q对它们分别q行探讨?

   一?理解U程

   要讲解线E,不得不说一下进E,q程是应用程序的执行实例Q每个进E是q有的虚拟地址I间、代码、数据和其它pȝ资源l成。进E在q行时创建的资源随着q程的终止而死亡。线E的基本思想很简单,它是一个独立的执行,是进E内部的一个独立的执行单元Q相当于一个子E序Q它对应Visual C++中的CwinThreadcȝ对象。单独一个执行程序运行时Q缺省的q行包含的一个主U程Q主U程以函数地址的Ş式,如main或WinMain函数Q提供程序的启动点,当主U程l止Ӟq程也随之终止,但根据需要,应用E序又可以分解成许多独立执行的线E,每个U程q行的运行在同一q程中?

   一个进E中的所有线E都在该q程的虚拟地址I间中,使用该进E的全局变量和系l资源。操作系l给每个U程分配不同的CPU旉片,在某一个时刻,CPU只执行一个时间片内的U程Q多个时间片中的相应U程在CPU内轮执行,׃每个旉片时间很短,所以对用户来说Q仿佛各个线E在计算Z是ƈ行处理的。操作系l是ҎU程的优先来安排CPU的时_优先U高的线E优先运行,优先U低的线E则l箋{待?

   U程被分ZU:用户界面U程和工作线E(又称为后台线E)。用L面线E通常用来处理用户的输入ƈ响应各种事g和消息,其实Q应用程序的L行线ECWinAPP对象是一个用L面线E,当应用程序启动时自动创徏和启动,同样它的l止也意味着该程序的l束Q进城终止。工作者线E用来执行程序的后台处理dQ比如计、调度、对串口的读写操作等Q它和用L面线E的区别是它不用从CwinThreadcL生来创徏Q对它来说最重要的是如何实现工作U程d的运行控制函数。工作线E和用户界面U程启动时要调用同一个函数的不同版本Q最后需要读者明白的是,一个进E中的所有线E共享它们父q程的变量,但同时每个线E可以拥有自q变量?
   二?U程的管理和操作

   1Q?U程的启?

   创徏一个用L面线E,首先要从cCwinThread产生一个派生类Q同时必M用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE来声明和实现q个CwinThreadzcR?

   W二步是Ҏ需要重载该zcȝ一些成员函数如QExitInstance()QInitInstance()QOnIdle();PreTranslateMessage(){函敎ͼ最后启动该用户界面U程Q调用AfxBeginThread()函数的一个版本:CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );其中W一个参Cؓ指向定义的用L面线E类指针变量Q第二个参数为线E的优先U,W三个参CؓU程所对应的堆栈大,W四个参CؓU程创徏时的附加标志Q缺省ؓ正常状态,如ؓCREATE_SUSPENDED则线E启动后为挂L态?

   对于工作U程来说Q启动一个线E,首先需要编写一个希望与应用E序的其余部分ƈ行运行的函数如Fun1()Q接着定义一个指向CwinThread对象的指针变?pThread,调用AfxBeginThread(Fun1,param,priority)函数Q返回glpThread变量的同时一q启动该U程来执行上面的Fun1()函数Q其中Fun1是线E要q行的函数的名字Q也既是上面所说的控制函数的名字,param是准备传送给U程函数Fun1的Q?2位|priority则是定义该线E的优先U别Q它是预定义的常敎ͼ读者可参考MSDN?

   2Q线E的优先U?

   以下的CwinThreadcȝ成员函数用于U程优先U的操作Q?

int GetThreadPriority();
BOOL SetThradPriority()(int nPriority);

上述的二个函数分别用来获取和讄U程的优先Q这里的优先U,是相对于该线E所处的优先权层ơ而言的,处于同一优先权层ơ的U程Q优先高的U程先运行;处于不同优先权层ơ上的线E,谁的优先权层ơ高Q谁先运行。至于优先讄所需的常敎ͼ自己参考MSDN可以了Q要注意的是要想讄U程的优先Q这个线E在创徏时必d有THREAD_SET_INFORMATION讉K权限。对于线E的优先权层ơ的讄QCwinThreadcL有提供相应的函数Q但是可以通过Win32 SDK函数GetPriorityClass()和SetPriorityClass()来实现?

   3Q线E的悬挂、恢?

   CwinThreadcM包含了应用程序悬挂和恢复它所创徏的线E的函数Q其中SuspendThread()用来悬挂U程Q暂停线E的执行QResumeThread()用来恢复U程的执行。如果你对一个线E连l若q次执行SuspendThread()Q则需要连l执行相应次的ResumeThread()来恢复线E的q行?

   4Q结束线E?

   l止U程有三U途径Q线E可以在自n内部调用AfxEndThread()来终止自w的q行Q可以在U程的外部调用BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode )来强行终止一个线E的q行Q然后调用CloseHandleQ)函数释放U程所占用的堆栈;W三U方法是改变全局变量QɾU程的执行函数返回,则该U程l止。下面以W三U方法ؓ例,l出部分代码Q?

////////////////////////////////////////////////////////////////
//////CtestView message handlers
/////Set to True to end thread
Bool bend=FALSE;//定义的全局变量Q用于控制线E的q行
//The Thread Function
UINT ThreadFunction(LPVOID pParam)//U程函数
{
while(!bend)
{Beep(100,100);
Sleep(1000);
}
return 0;
}
CwinThread *pThread;
HWND hWnd;
/////////////////////////////////////////////////////////////
Void CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
pThread=AfxBeginThread(ThradFunction,hWnd);//启动U程
pThread->m_bAutoDelete=FALSE;//U程为手动删?
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{ bend=TRUE;//改变变量Q线E结?
WaitForSingleObject(pThread->m_hThread,INFINITE);//{待U程l束
delete pThread;//删除U程
Cview::OnDestroy();
}
   三?U程之间的通信

   通常情况下,一个次U线E要ZU程完成某种特定cd的Q务,q就隐含着表示在主U程和次U线E之间需要徏立一个通信的通道。一般情况下Q有下面的几U方法实现这U通信dQ用全局变量Q上一节的例子其实使用的就是这U方法)、用事件对象、用消息。这里我们主要介l后两种Ҏ?

   1Q?利用用户定义的消息通信

   在WindowsE序设计中,应用E序的每一个线E都拥有自己的消息队列,甚至工作U程也不例外Q这样一来,׃得线E之间利用消息来传递信息就变的非常单。首先用戯定义一个用h息,如下所C:#define WM_USERMSG WMUSER+100Q在需要的时候,在一个线E中调用

Q:PostMessage((HWND)param,WM_USERMSG,0,0)
?
CwinThread::PostThradMessage()

来向另外一个线E发送这个消息,上述函数的四个参数分别是消息要发送到的目的窗口的句柄、要发送的消息标志W、消息的参数WPARAM和LPARAM。下面的代码是对上节代码的修改,修改后的l果是在U程l束时显CZ个对话框Q提C线E结束:

UINT ThreadFunction(LPVOID pParam)
{
while(!bend)
{
Beep(100,100);
Sleep(1000);
}
Q:PostMessage(hWnd,WM_USERMSG,0,0)Q?
return 0;
}
////////WM_USERMSG消息的响应函CؓOnThreadended(WPARAM wParam,LPARAM lParam)
LONG CTestView::OnThreadended(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Thread ended.");
Retrun 0;
}

上面的例子是工作者线E向用户界面U程发送消息,对于工作者线E,如果它的设计模式也是消息驱动的,那么调用者可以向它发送初始化、退出、执行某U特定的处理{消息,让它在后台完成。在控制函数中可以直接用:QGetMessage()q个SDK函数q行消息分检和处理,自己实现一个消息@环。GetMessage()函数在判断该U程的消息队列ؓI时Q线E将pȝ分配l它的时间片让给其它U程Q不无效的占用CPU的时_如果消息队列不ؓI,p取这个消息,判断q个消息的内容ƈq行相应的处理?

   2Q用事g对象实现通信

   在线E之间传递信可行通信比较复杂的方法是使用事g对象Q用MFC的Ceventcȝ对象来表C。事件对象处于两U状态之一Q有信号和无信号Q线E可以监视处于有信号状态的事gQ以便在适当的时候执行对事g的操作。上qC子代码修改如下:

////////////////////////////////////////////////////////////////////
Cevent threadStart,threadEnd;
////////////////////////////////////////////////////////////////////
UINT ThreadFunction(LPVOID pParam)
{
Q:WaitForSingleObject(threadStart.m_hObject,INFINITE);
AfxMessageBox("Thread start.");
while(!bend)
{
Beep(100,100);
Sleep(1000);
Int result=::WaitforSingleObject(threadEnd.m_hObject,0);
//{待threadEnd事g有信P无信hU程在这里悬?
If(result==Wait_OBJECT_0)
Bend=TRUE;
}
Q:PostMessage(hWnd,WM_USERMSG,0,0)Q?
return 0;
}
/////////////////////////////////////////////////////////////
Void CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
threadStart.SetEvent();//threadStart事g有信?
pThread=AfxBeginThread(ThreadFunction,hWnd);//启动U程
pThread->m_bAutoDelete=FALSE;
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{ threadEnd.SetEvent();
WaitForSingleObject(pThread->m_hThread,INFINITE);
delete pThread;
Cview::OnDestroy();
}

q行q个E序Q当关闭E序Ӟ才显C提C框Q显C?Thread ended"
   四?U程之间的同?

   前面我们讲过Q各个线E可以访问进E中的公共变量,所以用多U程的过E中需要注意的问题是如何防止两个或两个以上的线E同时访问同一个数据,以免破坏数据的完整性。保证各个线E可以在一起适当的协调工作称为线E之间的同步。前面一节介l的事g对象实际上就是一U同步Ş式。Visual C++中用同步类来解x作系l的q行性而引L数据不安全的问题QMFC支持的七个多U程的同步类可以分成两大c:同步对象QCsyncObject、Csemaphore、Cmutex、CcriticalSection和CeventQ和同步讉K对象QCmultiLock和CsingleLockQ。本节主要介l界区Qcritical sectionQ、互斥(mutexeQ、信号量QsemaphoreQ,q些同步对象使各个线E协调工作,E序q行h更安全?

   1Q?临界?

   临界区是保证在某一个时间只有一个线E可以访问数据的Ҏ。用它的过E中Q需要给各个U程提供一个共享的临界区对象,无论哪个U程占有临界区对象,都可以访问受C护的数据Q这时候其它的U程需要等待,直到该线E释放界区对象为止Q界区被释攑֐Q另外的U程可以强占q个临界区,以便讉K׃n的数据。界区对应着一个CcriticalSection对象Q当U程需要访问保护数据时Q调用界区对象的Lock()成员函数Q当对保护数据的操作完成之后Q调用界区对象的Unlock()成员函数释放对界区对象的拥有权Q以使另一个线E可以夺取界区对象q访问受保护的数据。同时启动两个线E,它们对应的函数分别ؓWriteThread()和ReadThread()Q用以对公共数组larray[]操作Q下面的代码说明了如何用界区对象Q?

#include "afxmt.h"
int array[10],destarray[10];
CCriticalSection Section;
////////////////////////////////////////////////////////////////////////
UINT WriteThread(LPVOID param)
{Section.Lock();
for(int x=0;x<10;x++)
array[x]=x;
Section.Unlock();
}
UINT ReadThread(LPVOID param)
{
Section.Lock();
For(int x=0;x<10;x++)
Destarray[x]=array[x];
Section.Unlock();
}

上述代码q行的结果应该是Destarray数组中的元素分别?-9Q而不是杂乱无章的敎ͼ如果不用同步,则不是这个结果,有兴的读者可以实验一下?
   2Q?互斥

   互斥与界区很相|但是使用时相对复杂一些,它不仅可以在同一应用E序的线E间实现同步Q还可以在不同的q程间实现同步,从而实现资源的安全׃n。互斥与Cmutexcȝ对象相对应,使用互斥对象Ӟ必须创徏一个CSingleLock或CMultiLock对象Q用于实际的讉K控制Q因里的例子只处理单个互斥,所以我们可以用CSingleLock对象Q该对象的Lock()函数用于占有互斥QUnlock()用于释放互斥。实C码如下:

#include "afxmt.h"
int array[10],destarray[10];
CMutex Section;

/////////////////////////////////////////////////////////////
UINT WriteThread(LPVOID param)
{ CsingleLock singlelock;
singlelock (&Section);
singlelock.Lock();
for(int x=0;x<10;x++)
array[x]=x;
singlelock.Unlock();
}
UINT ReadThread(LPVOID param)
{ CsingleLock singlelock;
singlelock (&Section);
singlelock.Lock();

For(int x=0;x<10;x++)
Destarray[x]=array[x];
singlelock.Unlock();

}

   3Q?信号?

   信号量的用法和互斥的用法很相|不同的是它可以同一时刻允许多个U程讉K同一个资源,创徏一个信号量需要用Csemaphorecd明一个对象,一旦创Z一个信号量对象Q就可以用它来对资源的访问技术。要实现计数处理Q先创徏一个CsingleLock或CmltiLock对象Q然后用该对象的Lock()函数减少q个信号量的计数|Unlock()反之。下面的代码分别启动三个U程Q执行时同时昄二个消息框,然后10U后W三个消息框才得以显C?

/////////////////////////////////////////////////////////////////
Csemaphore *semaphore;
Semaphore=new Csemaphore(2,2);
HWND hWnd=GetSafeHwnd();
AfxBeginThread(threadProc1,hWnd);
AfxBeginThread(threadProc2,hWnd);
AfxBeginThread(threadProc3,hWnd);
//////////////////////////////////////////////////////////////////////
UINT ThreadProc1(LPVOID param)
{CsingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread1 had access","Thread1",MB_OK);
return 0;
}
UINT ThreadProc2(LPVOID param)
{CSingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread2 had access","Thread2",MB_OK);
return 0;
}
UINT ThreadProc3(LPVOID param)
{CsingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread3 had access","Thread3",MB_OK);
return 0;
}


   对复杂的应用E序来说Q线E的应用l应用程序提供了高效、快速、安全的数据处理能力。本文讲qCU程中经帔R到的问题Q希望对读者朋友有一定的帮助?/TD>


]]>
99þþƷ鶹| þĻԴվ| ޵һƷƷþ| ˾þAV| ޵һAVվþþƷ˵AV | þùƷ| 99þù뾫ƷѾþþþ| þAV| þۺɫһ| ղþǿѵĿ| þۺϾɫۺվ| ձһþ | ղƷaëƬþ| Ʒþһ| þۺɫ| þþþAVרվ| ƷþþþaӰԺ| þ÷׾Ʒ | þ97㽶| þþŮ붯ȺëƬ| þþþ18| þˬˬAVƬ| þԭavapp| þþƷĻ| vaþþþ| þþþƷþþþþ | Ʒþþþþҹҹ| þˬˬAV| þþþAVרɫ| Avþ| þþþAVվ| ƷþþþþӰԺ| þҹҹ³³ƬӰ| ھƷþþþþþ| þ޾Ʒ˳ۺ| ŷ˼Ծþ| þúݺݰۺӰԺ| þݺҹҹ2020һ| ղƷþþþþþ| ùþþۺ| ޳˾þ|