《VC++深入详解》学习笔记[13]——第16章 线程同步与异步套接字编程

来源:互联网 发布:h5页面源码 编辑:程序博客网 时间:2024/05/23 23:20
 

第16章 线程同步与异步套接字编程

1.事件对象

       事件对象与互斥对象一样也属于内核对象。事件对象有两种不同的类型:

       ①人工重置的事件对象:当人工重置的事件对象得到通知时,等待该事件对象的所有线程均变为可调度线程。当线程等待到该对象的所有权之后,需要显示地调用ResetEvent函数手动将该事件对象设为无信号状态;

       ②自动重置的事件对象:当一个自动重置的事件对象得到通知时,等待该事件对象的线程中只有一个线程变为可调度线程。当线程得到该对象的所有权之后,系统会自动将该对象设置为无信号状态。

       为了实现线程间的同步,不应该使用人工重置的事件对象,而应该使用自动重置的事件对象。

代码分析:

#include <windows.h>

#include <iostream.h>

 

int ticket=100;

HANDLE g_hEvent;//保存事件对象句柄

 

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

 

void main()

{

       HANDLE thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

       HANDLE thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

      

       CloseHandle(thread1);

       CloseHandle(thread2);

       //创建事件对象,可用命名事件对象来控制只运行一个实例

       g_hEvent=CreateEvent(NULL,

              FALSE,   //TRUE人工重置,FALSE 自动重置

              FALSE,   //初始化状态,TURE信号状态,FALSE非信号状态

              "tickets");       //事件对象命名,NULL表示匿名

       if(g_hEvent)

       {

              if(ERROR_ALREADY_EXISTS == GetLastError())

              {

                     cout<<"Only one instance can run!"<<endl;

                     return;

              }

       }

       SetEvent(g_hEvent);//将事件设置为有信号状态

       Sleep(4000);

       CloseHandle(g_hEvent);

      

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

       WaitForSingleObject(g_hEvent,INFINITE);

       while(ticket)

       {

              cout<<"thread1 sells : "<<ticket--<<endl;

              Sleep(1);

              SetEvent(g_hEvent);

       }

       return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

       WaitForSingleObject(g_hEvent,INFINITE);

       while(ticket)

       {

              cout<<"thread2 sells : "<<ticket--<<endl;

              Sleep(1);

              SetEvent(g_hEvent);

       }

       return 0;

}

 

2.关键代码段

       关键代码段也称为临界区,工作在用户方式下。它是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。通常把多线程中访问同一种资源的那部分代码当做关键代码段。

代码分析:

#include "windows.h"

#include "iostream.h"

 

int ticket=100;

//HANDLE g_hEvent;

 

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

 

CRITICAL_SECTION  g_cs;//临界区对象

void main()

{

       HANDLE thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

       HANDLE thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

      

       CloseHandle(thread1);

       CloseHandle(thread2);

//初始化一个临界区对象

       InitializeCriticalSection(&g_cs);

      

       Sleep(4000);

// 释放一个没有被占有的临界区对象的所有资源

       DeleteCriticalSection(&g_cs);

      

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

       while(TRUE)

       {

              EnterCriticalSection(&g_cs);//等待临界区对象的所有权

                     //当调用线程赋予所有权进,本函数返回

                     // 如果一直没能等待到,那么导致线程暂停

              if(ticket>0)

              {

                     cout<<"thread1 sells : "<<ticket--<<endl;

                     Sleep(1);

              }

              else break;

              LeaveCriticalSection(&g_cs);//释放指定临界区对象所有权

       }

       return 0;

}

 

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

       while(TRUE)

       {

              EnterCriticalSection(&g_cs);

              if(ticket>0)

              {

                     cout<<"thread2 sells : "<<ticket--<<endl;

                     Sleep(1);

              }

              else break;

              LeaveCriticalSection(&g_cs);

       }

       return 0;

}

死锁:如果线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程二拥有了临界区对象B,等待临界区对象A的拥有权,这样就造成了死锁。

 

互斥对象、事件对象与关键代码段的比较:

1.互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步时,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。

2.关键代码段工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。

 

基于消息的异步套接字:

Windows套接字在两种模式下执行IO操作:阻塞模式和非阻塞模式。Windows Sockets为了支持Windows消息驱动机制,使应用程序开发者能够方便地处理网络通信,它对网络事件采用了基于消息的异步存储策略,即采用非阻塞方式实现网络应用程序。异步选择函数WSAAsyncSelect提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序响应的窗口函数将收到一个消息,消息中指示了发生的网络事件以及一些相关信息。

 

原创粉丝点击