9.1 等待函数

(1)WaitForSingleObject(hObject,dwMilliseonds);

  ①dwMilliseconds为INFINITE时表示无限等待

  ②dwMilliseconds=0时表示立即返回,即使它要等待的条件还没满足

  ③dwMilliseconds为其它值时(单位为ms),其返回值有三种情况:A、WAIT_OBJECT_0表示等待的对象触发。WAIT_TIMEOUT表示超时。WAIT_FAILED:表示可能传入了无效句柄,可进一步调用GetLastError来得到更详细的信息。

(2)WaitForMultipleObjects函数

参数

描述

nCount

要等待的内核对象的数量,这个值必须在1~MAXIMUM_WAIT_OBJECTS之间(即最多64个)

lpHandles

指向等待的内核对象的数组

bWaitAll

TRUE时表示等待的所有内核对象都触发FALSE:只要有一个触发就返回

dwMilliseconds

与WaitForSingleObject的含义相同

返回值

WAIT_FAILED:传入了无效句柄。

WAIT_TIMEOUT:超时

WAIT_OBJECT_0~WAIT_OBJECT_x:如果bWaitAll为TRUE时,则当所有对象被触发时返回WAIT_OBJECT_0;如果bWaitAll为FALSE,则只要有一个对象被触发,函数就返回WAIT_OBJECT_x,其中的x表示上述被触发对象在lpHandles数组中的下标,即用于通知是哪个对象被触发。

9.2 等待成功所引起的副作用

(1)WaitForSingleObject或WaitForMultipleObjects函数在调用成功后,如果对象的状态也被改变了,这称为“等待成功所引起的副作用”(注意调用失败并不会改变对象状态)。如事件对象(Event),当为“自动设置”时,在调用WaitForSingleObject成功以后会被自动恢复初始的为“无信号”状态,这就是副作用。当然如果设置为“手动”,则不会有这种副作用。此外,其他对象有的也有副作用,但有的完全没有。

(2)举例说明等待成功的副作用(设两个线程以完全相同的方式调用WaitForMultiple*函数)

HANDLE hEvent[2];

hEvent[0] = hAutoResetEvent1; //初始化时为“无信号”状态

hEvent[1] = hAutoResetEvent2; //初始化时为“无信号”状态

WaitForMultipleObjects(2,hEvent,TRUE,INFINITE);

  ①刚调用WaitFor*函数时,两个事件对象均未被触发,所以两个线程都进入等待状态。

  ②然后当hAutoResetEvent1被触发,但由于hAutoResetEvent2未被触发,所以系统不会唤醒任何一个线程,所以WaitFor*函数没有成功返回,不会对hAutoResetEvent1对象产生副作用。

  ③接下来hAutoResetEvent2被触发,此时两个线程中的一个检测到,并等待成功。这时该线程的WaitFor*函数会以原子方式将两个事件对象重设为“无信号”状态(即在改变两个事件状态的过程不允许被打断,此时其它线程无法访问和改变这两个事件对象,这种原子方式的特征是为了防止死锁的发生,见课本P236页)。但对于另一个线程来说,虽然他曾经检测到hAutoResetEvent1被触发,但现在看到的状态却是未触发,所以它会继续等待,直到这两个对象都被触发。这就是副作用,因为hAutoResetEvent曾经被触发过,而现在还要重新等待。

9.3 事件内核对象

(1)创建事件内核对象:CreateEvent函数

参数

描述

psa

安全属性(如使用计数、句柄继承等)

bManualReset

TRUE:手动重置事件,两个特点:①当事件被触发时,正在等待该事件的所有线程都变为可调度;②调用WaitFor*函数后,不会自动重置对象为“无信号”状态

FALSE:自动重置事件,两个特点:①当事件被触发时,只有一个正在等待该事件的线程变为可调度;②调用WaitFor*函数会,会自动将事件的状态重置为“无信号”

bInitialState

创建时的初始状态,TRUE为有信号,FALSE为无信号。

pszName

对象的名字

返回值

返回与当前进程相关的事件内核对象句柄

(2)CreateEventEx函数

参数

描述

psa

安全属性(如使用计数、句柄继承等)

pszName

对象的名字

dwFlags

CREATE_EVENT_INITIAL_SET(0x02):初始化为触发状态

CREATE_EVENT_MANUAL_RESET(0x01):手动重置事件

dwDesiredAccess

返回句柄的访问权限

返回值

返回与当前进程相关的事件内核对象句柄

(3)SetEvent——将事件设为触发状态

(4)ResetEvent——重置事件为未触发状态

★对于自动重置事件:当线程成功等到事件对象时,会自动重置为未触发状态,而不必调用ResetEvent函数

(5)PulseEvent函数——脉冲事件函数

  ①先触发事件然后立刻恢复到未触发状态,相当于调用SetEvent后立即调用ResetEvent。

  ②如果是手动重置事件调用PulseEvent,则会将所有正在等待事件的线程变成可调度状态。如果是自动重置事件,那么只有一个正在等待该事件的线程会变成可调度状态。

  ③如果当事件被脉冲触发的时候没有线程正在等待该事件,则不会产生任何效果

(6)手动重置事件与自动重置事件的比较

【伪代码分析】

  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <process.h>
  4. #include <tchar.h>
  5.  
  6. typedef unsigned(__stdcall *PTHREAD_START) (void *);
  7.  
  8. DWORD WINAPI WordCount(PVOID pvParam);
  9. DWORD WINAPI SpellCheck(PVOID pvParam);
  10. DWORD WINAPI GrammarCheck(PVOID pvParam);
  11.  
  12. HANDLE g_hEvent;
  13.  
  14. int _tmain()
  15. {
  16. //创建一个手动,未触发的事件
  17. g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  18.  
  19. //创建3个线程
  20. HANDLE hThread[];
  21. DWORD dwThreadID;
  22. hThread[] = (HANDLE)_beginthreadex(NULL, , (PTHREAD_START)WordCount,NULL, ,(unsigned *)&dwThreadID);
  23. hThread[] = (HANDLE)_beginthreadex(NULL, , (PTHREAD_START)SpellCheck,NULL, , (unsigned *)&dwThreadID);
  24. hThread[] = (HANDLE)_beginthreadex(NULL, , (PTHREAD_START)GrammarCheck,NULL, , (unsigned *)&dwThreadID);
  25.  
  26. //打开文件并读入内存
  27. OpenFileAndReadContentsIntoMemory(...);
  28.  
  29. //因为是手动重置事件,所以会同时唤醒3个线程
  30. //假设上面的Open*函数己经把数据读到内存中,则当3个线程被唤醒
  31. //时则会时候对该文件进行“字数统计”、“拼写检查”和“语法检查”
  32. //因为这3个操作都是读操作,所以可以同时进程。
  33. SetEvent(g_hEvent);
  34. ...
  35. return ;
  36. }
  37.  
  38. //“字数统计”
  39. DWORD WINAPI WordCount(PVOID pvParam)
  40. {
  41. //等待文件的数据被全部载入内存
  42. WaitForSingleObject(g_hEvent, INFINITE);
  43.  
  44. //访问内存块,因3个线程同时访问内存,所以这里只能是共享方式访问
  45. ...
  46. return ;
  47. }
  48.  
  49. //“拼写检查”
  50. DWORD WINAPI SpellCheck(PVOID pvParam)
  51. {
  52. //等待文件的数据被全部载入内存
  53. WaitForSingleObject(g_hEvent, INFINITE);
  54.  
  55. //访问内存块,因3个线程同时访问内存,所以这里只能是共享方式访问
  56. ...
  57. return ;
  58. }
  59.  
  60. //“语法检查”
  61. DWORD WINAPI GrammarCheck(PVOID pvParam)
  62. {
  63. //等待文件的数据被全部载入内存
  64. WaitForSingleObject(g_hEvent, INFINITE);
  65.  
  66. //访问内存块,因3个线程同时访问内存,所以这里只能是共享方式
  67. ...
  68. return ;
  69. }
  70.  
  71. //如果将以上的事件对象改为自动重置时,即
  72. g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  73.  
  74. //此时的只要唤醒其中一个线程
  75. SetEvent(g_hEvent);
  76.  
  77. //此时的各线程函数会改为如果的形式,这时只有一个线程被唤醒
  78. DWORD WINAPI WordCount\SpellCheck\GrammarCheck (PVOID pvParam)
  79. {
  80. //等待文件的数据被全部载入内存
  81. WaitForSingleObject(g_hEvent, INFINITE); //因为是自动对象,该函数调用后会自动被设为未触发
  82.  
  83. //访问内存块,因为此时只唤醒一个线程,所以对内存的访问可以是可读可写的
  84. ...
  85. SetEvent(g_hEvent); //唤醒剩下的2个线程中的一个,这句是与手动事件不同的,因为手动会全部唤醒,无需这句
  86. return ;
  87. }

【HandShake示例程序】——演示事件内核对象的使用,用于通知某项任务是否完成

  1. /************************************************************************
  2. Module:Handshake.cpp
  3. Notices:Copyright(c)2008 Jeffrey Richter & Christophe Nasarre
  4. ************************************************************************/
  5. #include "../../CommonFiles/CmnHdr.h"
  6. #include <tchar.h>
  7. #include "resource.h"
  8.  
  9. //////////////////////////////////////////////////////////////////////////
  10. //当客户端发出一个请求后,事件触发
  11. HANDLE g_hevtRequestSubmitted;
  12.  
  13. //当服务端发出一个结果给客户端时,事件触发
  14. HANDLE g_hevtResultReturned;
  15.  
  16. //客户线程与服务线程共享的缓冲区
  17. TCHAR g_szSharedRequestAndResultBuffer[];
  18.  
  19. //客户端发送一个特殊的字符串,用来结束程序
  20. TCHAR g_szServerShutdown[] = TEXT("Server Shutdown");
  21.  
  22. //主对话框句柄。当服务线程接收到关闭消息时,会检测该句柄是否有效
  23. HWND g_hMainDlg;
  24.  
  25. //////////////////////////////////////////////////////////////////////////
  26. DWORD WINAPI ServerThread(PVOID pvParam)
  27. {
  28. //假设服务线程没被停止
  29. BOOL fShutdown = FALSE;
  30.  
  31. while (!fShutdown){
  32.  
  33. //等待客户线程提交请求
  34. WaitForSingleObject(g_hevtRequestSubmitted, INFINITE);
  35.  
  36. //检查是否要结束程序(并关闭对话框时,会设置退出字符串)
  37. //这里检查g_hMainDlg==NULL,可以防止用户手动输入“Server Shutdown”字符串
  38. //而导致该线程退出的现象,也就是用户输入该字符串并不退出。
  39. fShutdown = (g_hMainDlg == NULL) &&
  40. (_tcscmp(g_szSharedRequestAndResultBuffer, g_szServerShutdown) == );
  41.  
  42. if (!fShutdown){
  43. //处理客户线程的请求
  44. _tcsrev(g_szSharedRequestAndResultBuffer); //反转字符串
  45. }
  46.  
  47. //通知客户线程,服务线程己处理完毕
  48. SetEvent(g_hevtResultReturned);
  49. }
  50.  
  51. //客户线程要求退出
  52. return ;
  53. }
  54.  
  55. //////////////////////////////////////////////////////////////////////////
  56. BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
  57. {
  58. chSETDLGICONS(hwnd, IDI_HANDSHAKE);
  59.  
  60. //初始化编辑框
  61. Edit_SetText(GetDlgItem(hwnd, IDC_REQUEST), TEXT("Some test data"));
  62.  
  63. g_hMainDlg = hwnd;
  64. return TRUE;
  65. }
  66.  
  67. void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotify)
  68. {
  69. switch (id)
  70. {
  71. case IDCANCEL:
  72. EndDialog(hwnd, id);
  73. break;
  74. case IDC_SUBMIT:
  75.  
  76. //将客户请求的字符串拷入共享缓冲区
  77. Edit_GetText(GetDlgItem(hwnd, IDC_REQUEST),
  78. g_szSharedRequestAndResultBuffer,_countof(g_szSharedRequestAndResultBuffer));
  79. //客户线程提交请求,表示缓冲区己准备好。并等待服务线程返回处理结果
  80. SignalObjectAndWait(g_hevtRequestSubmitted,
  81. g_hevtResultReturned, INFINITE,FALSE);
  82.  
  83. //显示服务线程的处理结果
  84. Edit_SetText(GetDlgItem(hwnd, IDC_RESULT), g_szSharedRequestAndResultBuffer);
  85. break;
  86. }
  87. }
  88.  
  89. INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  90. {
  91. switch (uMsg)
  92. {
  93. chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
  94. chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
  95. }
  96. return FALSE;
  97. }
  98.  
  99. int WINAPI _tWinMain(HINSTANCE hInstExe,HINSTANCE,PTSTR,int)
  100. {
  101. //创建并初始化2个未触发的自动事件对象
  102. g_hevtRequestSubmitted = CreateEvent(NULL, FALSE, FALSE, NULL);
  103. g_hevtResultReturned = CreateEvent(NULL, FALSE, FALSE, NULL);
  104.  
  105. //创建服务线程
  106. DWORD dwThreadID;
  107. HANDLE hThreadServer = chBEGINTHREADEX(NULL, , ServerThread, NULL, , &dwThreadID);
  108.  
  109. //运行UI线程(主线程,也是客户线程)
  110. DialogBox(hInstExe, MAKEINTRESOURCE(IDD_HANDSHAKE), NULL, Dlg_Proc);
  111. g_hMainDlg = NULL; //表示对话框己退出
  112.  
  113. //主线程UI线程正在关闭,这时服务线程可能被阻塞在Wait*函数中,将其唤醒,
  114. //并填写退出字符串,以便退出
  115. _tcscpy_s(g_szSharedRequestAndResultBuffer,
  116. _countof(g_szSharedRequestAndResultBuffer),g_szServerShutdown);
  117. SetEvent(g_hevtRequestSubmitted);
  118.  
  119. //等待服务线程退出
  120. HANDLE h[];
  121. h[] = g_hevtResultReturned;
  122. h[] = hThreadServer;
  123. WaitForMultipleObjects(, h, TRUE, INFINITE);
  124.  
  125. //关闭所有的句柄
  126. CloseHandle(g_hevtResultReturned);
  127. CloseHandle(g_hevtRequestSubmitted);
  128. CloseHandle(hThreadServer);
  129. return ;
  130. }

//resource.h

  1. //{{NO_DEPENDENCIES}}
  2. // Microsoft Visual C++ 生成的包含文件。
  3. // 供 9_HandShake.rc 使用
  4. //
  5. #define IDD_HANDSHAKE 101
  6. #define IDI_HANDSHAKE 102
  7. #define IDC_REQUEST 1002
  8. #define IDC_RESULT 1003
  9. #define IDC_SUBMIT 1004
  10.  
  11. // Next default values for new objects
  12. //
  13. #ifdef APSTUDIO_INVOKED
  14. #ifndef APSTUDIO_READONLY_SYMBOLS
  15. #define _APS_NEXT_RESOURCE_VALUE 103
  16. #define _APS_NEXT_COMMAND_VALUE 40001
  17. #define _APS_NEXT_CONTROL_VALUE 1003
  18. #define _APS_NEXT_SYMED_VALUE 101
  19. #endif
  20. #endif

//Handshake.rc

  1. // Microsoft Visual C++ generated resource script.
  2. //
  3. #include "resource.h"
  4.  
  5. #define APSTUDIO_READONLY_SYMBOLS
  6. /////////////////////////////////////////////////////////////////////////////
  7. //
  8. // Generated from the TEXTINCLUDE 2 resource.
  9. //
  10. #include "winres.h"
  11.  
  12. /////////////////////////////////////////////////////////////////////////////
  13. #undef APSTUDIO_READONLY_SYMBOLS
  14.  
  15. /////////////////////////////////////////////////////////////////////////////
  16. // 中文(简体,中国) resources
  17.  
  18. #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
  19. LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
  20.  
  21. #ifdef APSTUDIO_INVOKED
  22. /////////////////////////////////////////////////////////////////////////////
  23. //
  24. // TEXTINCLUDE
  25. //
  26.  
  27. TEXTINCLUDE
  28. BEGIN
  29. "resource.h\0"
  30. END
  31.  
  32. TEXTINCLUDE
  33. BEGIN
  34. "#include ""winres.h""\r\n"
  35. "\0"
  36. END
  37.  
  38. TEXTINCLUDE
  39. BEGIN
  40. "\r\n"
  41. "\0"
  42. END
  43.  
  44. #endif // APSTUDIO_INVOKED
  45.  
  46. /////////////////////////////////////////////////////////////////////////////
  47. //
  48. // Dialog
  49. //
  50.  
  51. IDD_HANDSHAKE DIALOGEX , , ,
  52. STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU
  53. CAPTION "Handshake"
  54. FONT , "MS Shell Dlg", , , 0x1
  55. BEGIN
  56. DEFPUSHBUTTON "提交到服务端",IDC_SUBMIT,,,,
  57. GROUPBOX "客户端",IDC_STATIC,,,,
  58. LTEXT "请求:",IDC_STATIC,,,,
  59. LTEXT "结果:",IDC_STATIC,,,,
  60. EDITTEXT IDC_REQUEST,,,,,ES_AUTOHSCROLL
  61. EDITTEXT IDC_RESULT,,,,,ES_AUTOHSCROLL | ES_READONLY
  62. END
  63.  
  64. /////////////////////////////////////////////////////////////////////////////
  65. //
  66. // DESIGNINFO
  67. //
  68.  
  69. #ifdef APSTUDIO_INVOKED
  70. GUIDELINES DESIGNINFO
  71. BEGIN
  72. IDD_HANDSHAKE, DIALOG
  73. BEGIN
  74. END
  75. END
  76. #endif // APSTUDIO_INVOKED
  77.  
  78. /////////////////////////////////////////////////////////////////////////////
  79. //
  80. // Icon
  81. //
  82.  
  83. // Icon with lowest ID value placed first to ensure application icon
  84. // remains consistent on all systems.
  85. IDI_HANDSHAKE ICON "Handshake.ico"
  86. #endif // 中文(简体,中国) resources
  87. /////////////////////////////////////////////////////////////////////////////
  88.  
  89. #ifndef APSTUDIO_INVOKED
  90. /////////////////////////////////////////////////////////////////////////////
  91. //
  92. // Generated from the TEXTINCLUDE 3 resource.
  93. //
  94.  
  95. /////////////////////////////////////////////////////////////////////////////
  96. #endif // not APSTUDIO_INVOKED

【Event程序】模拟共享区的读取

  1. #include <windows.h>
  2. #include <tchar.h>
  3. #include <locale.h>
  4.  
  5. //////////////////////////////////////////////////////////////////////////
  6. #define THREADCNT 6
  7. HANDLE g_hWriteEvent = NULL;
  8. HANDLE g_hThreads[THREADCNT] = { NULL };
  9.  
  10. //////////////////////////////////////////////////////////////////////////
  11. DWORD WINAPI ThreadProc(PVOID pvParam);
  12. void CreateEventsAndThreads(void);
  13. void WriteToBuffer(void);
  14. void CloseEvents();
  15.  
  16. int _tmain()
  17. {
  18. _tsetlocale(LC_ALL, _T("chs"));
  19. CreateEventsAndThreads();
  20. WriteToBuffer(); //将数据写入缓冲区。写完后,通过SetEvent通知各线程去读取
  21. _tprintf(_T("\n"));
  22.  
  23. DWORD dwWaitResult;
  24. dwWaitResult = WaitForMultipleObjects(THREADCNT, g_hThreads, TRUE, INFINITE);
  25. switch (dwWaitResult)
  26. {
  27. case WAIT_OBJECT_0: //因为bWaitAll为TRUE,所以当全部对象被触发,会返回Wait_object_0;
  28. _tprintf(_T("\n子线程全部结束!\n"));
  29. break;
  30. default:
  31. _tprintf(_T("\nWaitForMulipleObjects错误(%d)\n"), GetLastError());
  32. break;
  33.  
  34. }
  35.  
  36. CloseEvents();
  37. _tsystem(_T("PAUSE"));
  38. return ;
  39. }
  40.  
  41. DWORD WINAPI ThreadProc(PVOID pvParam)
  42. {
  43. DWORD dwWaitResult = ;
  44. _tprintf(_T("线程[%d]正在等待“写事件”被触发\n"),GetCurrentThreadId());
  45. dwWaitResult = WaitForSingleObject(g_hWriteEvent, INFINITE);
  46.  
  47. switch (dwWaitResult)
  48. {
  49. case WAIT_OBJECT_0:
  50. _tprintf(_T("线程[%d]正在读取缓冲区中的数据...\n"), GetCurrentThreadId());
  51. break;
  52.  
  53. default:
  54. _tprintf(_T("等待错误(%d)\n"), GetLastError());
  55. break;
  56. }
  57. _tprintf(_T("线程[%d]退出!\n"), GetCurrentThreadId());
  58. return ;
  59. }
  60.  
  61. void CreateEventsAndThreads(void)
  62. {
  63. //创建手动重置事件,这可以使所有线程都被唤醒。
  64. //如果改为自动,那么每次只能唤醒一个线程,程序会出现问题
  65. //这就是成功等待的副作用
  66. g_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, _T("WriteEvent"));
  67.  
  68. for (int i = ; i < THREADCNT;i++){
  69. g_hThreads[i] = CreateThread(NULL, , ThreadProc, NULL, , NULL);
  70. }
  71. }
  72.  
  73. void WriteToBuffer(void)
  74. {
  75. _tprintf(_T("主线程正在写入数据到共享区...\n"));
  76.  
  77. if (!SetEvent(g_hWriteEvent)){
  78. _tprintf(_T("SetEvent失败(%d)\n"),GetLastError());
  79. }
  80. }
  81.  
  82. void CloseEvents()
  83. {
  84. CloseHandle(g_hWriteEvent);
  85. }

第9章 用内核对象进行线程同步(1)_事件对象(Event)的更多相关文章

  1. Windows核心编程:第9章 用内核对象进行线程同步

    Github https://github.com/gongluck/Windows-Core-Program.git //第9章 用内核对象进行线程同步.cpp: 定义应用程序的入口点. // #i ...

  2. windows核心编程---第八章 使用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  3. C++之内核对象进行线程同步

    用户模式下的线程同步机制提供了非常好的性能,但他们也的确存在一些局限性,而且不适用于许多应用程序,例如,对Interlocked系列函数只能对一个值进行操作,它们从来不会把线程切换到等待状态.我们可以 ...

  4. 《Windows核心编程系列》八谈谈用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  5. 操作系统中的进程同步与Window中利用内核对象进行线程同步的关系

    操作系统中为了解决进程间同步问题提出了用信号量机制,信号量可分为四种类型分别是互斥型信号量,记录型信号量,AND型信号量,信号量集. 互斥型信号量 互斥型信号量是资源数量为1的特殊的记录型信号量.表示 ...

  6. [C++] socket - 6 [API互斥事件对象实现线程同步]

    /*API互斥事件对象实现线程同步*/ #include<windows.h> #include<stdio.h> DWORD WINAPI myfun1(LPVOID lpP ...

  7. [C++] socket - 5 [API事件对象实现线程同步]

    /*API事件对象实现线程同步*/ #include<windows.h> #include<stdio.h> DWORD WINAPI myfun1(LPVOID lpPar ...

  8. Windows核心编程:第8章 用户模式下的线程同步

    Github https://github.com/gongluck/Windows-Core-Program.git //第8章 用户模式下的线程同步.cpp: 定义应用程序的入口点. // #in ...

  9. 第9章 用内核对象进行线程同步(4)_死锁(DeadLock)及其他

    9.7 线程同步对象速查表 对象 何时处于未触发状态 何时处于触发状态 成功等待的副作用 进程 进程仍在运行的时候 进程终止的时(ExitProcess.TerminateProcess) 没有 线程 ...

  10. 第9章 用内核对象进行线程同步(2)_可等待计时器(WaitableTimer)

    9.4 可等待的计时器内核对象——某个指定的时间或每隔一段时间触发一次 (1)创建可等待计时器:CreateWaitableTimer(使用时应把常量_WIN32_WINNT定义为0x0400) 参数 ...

随机推荐

  1. Office 365 - SharePoint 2013 Online 中创建母版页

    1.登陆SharePoint Online站点,点击右上角的设置按钮,如下图: 2.点击进入网站设置,到下面两个地方开启SharePoint Server 发布基础架构: 网站集管理 – 网站集功能 ...

  2. VPN常见错误码(633,628,691)的意思及修复方法

    因为工作原因经常上国外网站需要用到VPN,在这里总结一下使用中可能遇到的一些常见问题.(目前用Nydus觉得还不错) 1.633错误 :由于Windows系统本身的问题,在PPTP协议连接多次并断开之 ...

  3. sqlite使用xcode编译

    使用xcode去开发makefile的project(Building Makefile Projects With Xcode) 新建工程,template选择 external Build Sys ...

  4. android中Post方式发送HTTP请求

    Post方式比Get方式要复杂一点,因为该方式需要将请求的参数放在http请求的正文中,所以需要构造请求体. 步骤: 1.构造URL URL url = new URL(PATH); 2.设置连接 H ...

  5. iOS开发-生成随机数

    有时候我们需要在程序中生成随机数,但是在Objective-c中并没有提供相应的函数,好在C中提供了rand().srand().random().arc4random()几个函数.那么怎么使用呢?下 ...

  6. Mac终端常见命令

    Mac的使用 1.Finder资源管理器 Mac的硬盘不分区,只有一个根目录 2.系统偏好设置-控制面板 快捷键: 1.win下面的快捷键由ctrl换成common开头: commond+space ...

  7. java 实现(代码) -- 水仙花数 + 杨辉三角形

    /* 在控制台输出所有的“水仙花数” 水仙花:100-999 在以上数字范围内:这个数=个位*个位*个位+十位*十位*十位+百位*百位*百位 例如:xyz=x^3 +y^3 +z^3 怎么把三位数字拆 ...

  8. Java 线程异常处理器

    Thread.UncaughtExceptionHandler 是Thread类的一个静态内部接口,该接口只有一个方法: void uncaughtException(Thread t, Throwa ...

  9. 使用 PHPMailer 发送邮件

    转载 http://blog.csdn.net/liruxing1715/article/details/7914974 PHPMailer 的官方网站:http://phpmailer.worxwa ...

  10. VS2015 Git 插件使用教程

    VS2015 中继承了 Git 插件,再也不用下载 Github for Windows了. 从 团队-管理连接  中打开 团队资源管理器 克隆Repository 在 本地 Git 存储库下面点击 ...