一、事件
     1、创建事件
          HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,
                 BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);
          参数: lpEventAttributes:指向SECURITY_ATTRIBUTES结构体的指针,
                                    如果其值为NULL,则使用默认的安全性。
                 bManualReset:指定创建的是人工重置事件对象,还是自动重
                               置事件对象。如果是人工重置对象,当线程等
                               待到该对象的所有权限之后,需要调用ResetEvent
                               函数手动地将该事件设置为无信号状态;如果是自动
                               重置事件对象,当线程等到该对象的所有权之后,
                               系统会自动将该对象设置为无信号状态。
                 bInitialState: 指定事件对象的初始状态,如果此参数值为真,
                                那么该事件对象初始是有信号状态,否则是无
                                信号状态。
                 lpName:       指定事件对象的名称,如果此参数为NULL,
                               那么将创建一个匿名的事件对象。
     2、设置事件对象状态
          SetEvent函数将把指定的事件对象设置为有信号状态,该函数的原型如下:
          BOOL SetEvent(HANDLE hEvent);  该参数指定将要设置其状态的事件对象的句柄。
     3、重置事件对象状态
          ReseEvent函数将把指定的事件对象设置为无信号状态,该函数的原型如下:
          BOOL ResetEvent(HANDLE hEvent);         该参数指定将要重置其状态的事件对象句柄。
                                                  如果成功返回非0.否则返回0.
     4、使用事件对象实现线程同步
          DWORD WINAPI Fun1Proc(LPVOID lpParameter);
          DWORD WINAPI Fun2Proc(LPVOID lpParameter); 
         
          int tickets=100;
          HANDLE g_hEvent;
         
          void main()
          {
             HANDLE hThread1;
             HANDLE hThread2;
            
             g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
             hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
             hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
            
             CloseHandle(hThread1);
             CloseHandle(hThread2);
            
             Sleep(4000);
             CloseHandle(g_hEvent);
          }    
          DWORD WINAPI Fun1Proc(LPVOID lpParameter)
          {
              while(TRUE)
              {
                 WaitForSingleObject(g_hEvent,INFINITE);
                 if(tickets>0)
                 {
                    Sleep(1);
                    cout<<"thread1 sell ticket:"<<tickets--<<endl;
                    SetEvent(g_hEvent);
                 }
                 else
                 {
                    SetEvent(g_hEvent);
                    break;
                 }
              }
              return 0;
          }
          DWORD WINAPI Fun2Proc(LPVOID lpParameter)
          {
              while(TRUE)
              {
                 WaitForSingleObject(g_hEvent,INFINITE);
                 if(tickets>0)
                 {
                    Sleep(1);
                    cout<<"thread1 sell ticket:"<<tickets--<<endl;
                    SetEvent(g_hEvent);
                 }
                 else
                 {
                    SetEvent(g_hEvent);
                    break;
                 }
              }
              return 0;         
          }
          总结:当人工重置的事件对象得到通知时,等待该事件对象的所有线程均变
                成为可调度线程;当一个自动重置的事件对象得到通知时,等待该事
                件对象的线程中只有一个线程变为可调度线程,同时操作系统会将该
                事件对象设置为无信号状态,这样,当对所保护的代码执行完成后,
                需要调用SetEvent函数将该事件对象设置为有信号状态,而人工重置
                的事件对象,在一个线程得到该事件对象之后,操作系统并不会将该
                事件对象设置为无信号状态,除非显式地调用ReseEvent函数将其设置
                为无信号状态,否则该对象会一直是有信号状态。
     5、保证应用程序只有一个实例运行
          g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");
          if(g_hEvent)
          {
             if(ERROR_ALREADY_EXISTS==GetLastError())
             {
                cout<<"only one instance can run!"<<endl;
                return;
             }
          }
二、临界区
     1、相关函数
          void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
          void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
          void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
          void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
         
     2、实例
          DWORD WINAPI Fun1Proc(LPVOID lpParameter);
          DWORD WINAPI Fun2Proc(LPVOID lpParameter);
         
          int tickets=100;
          CRITICAL_SECTION g_cs;
         
          void main()
          {
             HANDLE hThread1;
             HANDLE hThread2;
            
             g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
             hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
             hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
            
             CloseHandle(hThread1);
             CloseHandle(hThread2);
            
             InitializeCriticalSection(&g_cs);
             Sleep(4000);
             DeleteCriticalSection(&g_cs);
          }    
          DWORD WINAPI Fun1Proc(LPVOID lpParameter)
          {
              while(TRUE)
              {
                 EnterCriticalSection(&g_cs);
                 Sleep(1);
                 if(tickets>0)
                 {
                    Sleep(1);
                    cout<<"thread1 sell ticket:"<<tickets--<<endl;
                    LeaveCriticalSection(&g_cs);
                 }
                 else
                 {
                    LeaveCriticalSection(&g_cs);
                    break;
                 }
              }
              return 0;
          }
          DWORD WINAPI Fun2Proc(LPVOID lpParameter)
          {
              while(TRUE)
              {
                 EnterCriticalSection(&g_cs);
                 Sleep(1);
                 if(tickets>0)
                 {
                    Sleep(1);
                    cout<<"thread1 sell ticket:"<<tickets--<<endl;
                    LeaveCriticalSection(&g_cs);
                 }
                 else
                 {
                    LeaveCriticalSection(&g_cs);
                    break;
                 }
              }
              return 0;         
          }            
三、线程死锁
     对多线程来说,人工线程1拥有了临界区对象A,等待临界区对象B的拥有权,
     线程2拥有了临界区对象B,等待临界区对象A的拥有权,这就造成了死锁。     
    
四、互斥,事件,临界的比较
     1、互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步时,
        速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多进
        程中的各个线程间进行同步。
     2、临界在用户方式下,同步速度较快,但是使用临界时,很容易进入死
        锁状态,因为在等待进入临界时无法设定超时值。  
五、基于消息的异步套接字
     1、相关函数说明
          指定的套接字请求基于Windows消息的网络事件通知,并自动将该套接字
          设置为非阻塞模式。
          int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg, long lEvent);
          参数: s:     标识请求网络事件通知的套接字描述符。
                 hWnd:   标识一个网络事件发生时接收消息的窗口的句柄。
                 wMsg:   指定网络事件发生时窗口将接收到的消息。
                 lEvent: 指定应用程序感兴趣的网络事件。