线程同步与异步套接字

  1. 事件对象

  2. 事件对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。
  3. 两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。

    The CreateEvent function creates or opens a named or unnamed event object.

    HANDLE CreateEvent(
      LPSECURITY_ATTRIBUTES lpEventAttributes,
      BOOL bManualReset,
      BOOL bInitialState,
      LPCTSTR lpName
    );

     

    The ResetEvent function sets the specified event object to the nonsignaled state.

    BOOL ResetEvent(
      HANDLE hEvent
    );

     

    The SetEvent function sets the specified event object to the signaled state.

    BOOL SetEvent(
      HANDLE hEvent
    );

     

    事件对象代码

    #include <windows.h>

    #include <iostream.h>

     

    DWORD WINAPI Fun1Proc(

      LPVOID lpParameter   // thread data

    );

     

    DWORD WINAPI Fun2Proc(

      LPVOID lpParameter   // thread data

    );

     

    int tickets=100;

    HANDLE g_hEvent;

     

    void main()

    {

    HANDLE hThread1;

    HANDLE hThread2;

    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

    CloseHandle(hThread1);

    CloseHandle(hThread2);

     

    g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");// 自动重置

    if(g_hEvent)

    {

    if(ERROR_ALREADY_EXISTS==GetLastError()) // 仅仅运行一个实例

    {

    cout<<"only instance can run!"<<endl;

    return;

    }

    }

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

     

    Sleep(4000);

    CloseHandle(g_hEvent);

    }

     

    DWORD WINAPI Fun1Proc(

      LPVOID lpParameter   // thread data

    )

    {

    while(TRUE)

    {

    WaitForSingleObject(g_hEvent,INFINITE);

    if(tickets>0)

    {

    Sleep(1);

    cout<<"thread1 sell ticket : "<<tickets--<<endl;

    }

    else

    break;

    SetEvent(g_hEvent);

    }

     

    return 0;

    }

     

    DWORD WINAPI Fun2Proc(

      LPVOID lpParameter   // thread data

    )

    {

     

    while(TRUE)

    {

    WaitForSingleObject(g_hEvent,INFINITE);

    if(tickets>0)

    {

    Sleep(1);

    cout<<"thread2 sell ticket : "<<tickets--<<endl;

    }

    else

    break;

    SetEvent(g_hEvent);

    }

     

    return 0;

    }

     

     

     

     

     

     

  4. 关键代码段
  5. 关键代码段(临界区)工作在用户方式下。
  6. 关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权

    The InitializeCriticalSection function initializes a critical section object.

    void InitializeCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection
    );

    The DeleteCriticalSection function releases all resources used by an unowned critical section object.

    void DeleteCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection
    );

    The EnterCriticalSection function waits for ownership of the specified critical section object. The function returns when the calling thread is granted ownership.

    void EnterCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection
    );

    The LeaveCriticalSection function releases ownership of the specified critical section object.

    void LeaveCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection
    );

     

    临界区代码

    #include <windows.h>

    #include <iostream.h>

     

    DWORD WINAPI Fun1Proc(

      LPVOID lpParameter   // thread data

    );

     

    DWORD WINAPI Fun2Proc(

      LPVOID lpParameter   // thread data

    );

     

    int tickets=100;

     

    CRITICAL_SECTION g_csA;

    CRITICAL_SECTION g_csB;

     

    void main()

    {

    HANDLE hThread1;

    HANDLE hThread2;

    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

    CloseHandle(hThread1);

    CloseHandle(hThread2);

     

    InitializeCriticalSection(&g_csA);

    InitializeCriticalSection(&g_csB);

    Sleep(4000);

     

    DeleteCriticalSection(&g_csA);

    DeleteCriticalSection(&g_csB);

    }

     

    DWORD WINAPI Fun1Proc(

      LPVOID lpParameter   // thread data

    )

    {

    while(TRUE)

    {

    EnterCriticalSection(&g_csA);

    Sleep(1);

    EnterCriticalSection(&g_csB); // 死锁

    if(tickets>0)

    {

    Sleep(1);

    cout<<"thread1 sell ticket : "<<tickets--<<endl;

    }

    else

    break;

    LeaveCriticalSection(&g_csB);

    LeaveCriticalSection(&g_csA);

    }

     

    return 0;

    }

     

    DWORD WINAPI Fun2Proc(

      LPVOID lpParameter   // thread data

    )

    {

     

    while(TRUE)

    {

    EnterCriticalSection(&g_csB); 

    Sleep(1);

    EnterCriticalSection(&g_csA); // 死锁

    if(tickets>0)

    {

    Sleep(1);

    cout<<"thread2 sell ticket : "<<tickets--<<endl;

    }

    else

    break;

    LeaveCriticalSection(&g_csA);

    LeaveCriticalSection(&g_csB);

    }

    cout<<"thread2 is running!"<<endl;

    return 0;

    }

     

     

     

  7. 线程死锁
  8. 哲学家进餐的问题
  9. 线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A的拥有权,就造成了死锁。
  10. 互斥对象、事件对象与关键代码段的比较
  11. 互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
  12. 关键代码段工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。
  13. 线程同步,首选使用关键代码段方式;使用多个临界区注意死锁
  14. 多个进程多个线程之间通信,可用互斥对象

     

     

  15. 基于消息的异步套接字

     

  16. Windows套接字在两种模式下执行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(将控制权交还给程序)。而在非阻塞模式下,Winsock函数无论如何都会立即返回。
  17. Windows Sockets为了支持Windows消息驱动机制,使应用程序开发者能够方便地处理网络通信,它对网络事件采用了基于消息的异步存取策略。
  18. Windows Sockets的异步选择函数WSAAsyncSelect()提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。

     

    The WSAAsyncSelect function requests Windows message-based notification of network events for a socket.

    int WSAAsyncSelect(
      SOCKET s,
      HWND hWnd,
      unsigned int wMsg,
      long lEvent
    );

     

  19. 相关函数说明

    int WSAEnumProtocols( LPINT lpiProtocols, LPWSAPROTOCOL_INFO lpProtocolBuffer, ILPDWORDlpdwBufferLength );

    Win32平台支持多种不同的网络协议,采用Winsock2,就可以编写可直接使用任何一种协议的网络应用程序了。通过WSAEnumProtocols函数可以获得系统中安装的网络协议的相关信息

  20. lpiProtocols一个以NULL结尾的协议标识号数组。这个参数是可选的,如果lpiProtocolsNULL,则返回所有可用协议的信息,否则,只返回数组中列出的协议信息。
  21. lpProtocolBuffer[out]一个用WSAPROTOCOL_INFO结构体填充的缓冲区。 WSAPROTOCOL_INFO结构体用来存放或得到一个指定协议的完整信息。
  22. lpdwBufferLength[in, out]在输入时,指定传递给WSAEnumProtocols()函数的lpProtocolBuffer缓冲区的长度;在输出时,存有获取所有请求信息需传递给WSAEnumProtocols ()函数的最小缓冲区长度。这个函数不能重复调用,传入的缓冲区必须足够大以便能存放所有的元素。这个规定降低了该函数的复杂度,并且由于一个 机器上装载的协议数目往往是很少的,所以并不会产生问题。

    SOCKET WSASocket( int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORDdwFlags );

  23. 前三个参数和socket()函数的前三个参数含义一样。
  24. lpProtocolInfo一个指向WSAPROTOCOL_INFO结构体的指针,该结构定义了所创建的套接字的特性。如果lpProtocolInfoNULL,则WinSock2 DLL使用前三个参数来决定使用哪一个服务提供者,它选择能够支持规定的地址族、套接字类型和协议值的第一个传输提供者。如果lpProtocolInfo不为NULL,则套接字绑定到与指定的结构WSAPROTOCOL_INFO相关的提供者。
  25. g保留的。
  26. dwFlags套接字属性的描述。

    int WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd,LPDWORD lpFlags, struct sockaddr FAR *lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );

  27. s标识套接字的描述符。
  28. lpBuffers[in, out]一个指向WSABUF结构体的指针。每一个WSABUF结构体包含一个缓冲区的指针和缓冲区的长度。
  29. dwBufferCount, lpBuffers数组中WSABUF结构体的数目。
  30. lpNumberOfBytesRecvd[out]如果接收操作立即完成,则为一个指向本次调用所接收的字节数的指针。
  31. lpFlags[in, out]一个指向标志位的指针。
  32. lpFrom[out]可选指针,指向重叠操作完成后存放源地址的缓冲区。
  33. lpFromlen[in, out]指向from缓冲区大小的指针,仅当指定了lpFrom才需要。
  34. lpOverlapped一个指向WSAOVERLAPPED结构体的指针(对于非重叠套接字则忽略)
  35. lpCompletionRoutine一个指向接收操作完成时调用的完成例程的指针(对于非重叠套接字则忽略)

    int WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent,DWORD dwFlags, const struct sockaddr FAR *lpTo, int iToLen, LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );

  36. s标识一个套接字(可能已连接)的描述符。
  37. lpBuffers一个指向WSABUF结构体的指针。每一个WSABUF结构体包含一个缓冲区的指针和缓冲区的长度。
  38. dwBufferCount, lpBuffers数组中WSABUF结构体的数目。
  39. lpNumberOfBytesSent[out]如果发送操作立即完成,则为一个指向本次调用所发送的字节数的指针。
  40. dwFlags指示影响操作行为的标志位。
  41. lpTo可选指针,指向目标套接字的地址。
  42. iToLenlpTo中地址的长度。
  43. lpOverlapped一个指向WSAOVERLAPPED结构的指针(对于非重叠套接字则忽略)
  44. lpCompletionRoutine一个指向接收操作完成时调用的完成例程的指针(对于非重叠套接字则忽略)

     

    The WSABUF structure enables the creation or manipulation of a data buffer.

    typedef struct __WSABUF {

    u_long len;

    char FAR* buf;
    } WSABUF,

    *LPWSABUF;

     

    The hostent structure is used by functions to store information about a given host, such as host name, IP address, and so forth. An application should never attempt to modify this structure or to free any of its components. Furthermore, only one copy of the hostentstructure is allocated per thread, and an application should therefore copy any information that it needs before issuing any other Windows Sockets API calls.

    typedef struct hostent {

    char FAR* h_name;

    char FAR  FAR** h_aliases;

    short h_addrtype;

    short h_length;

    char FAR  FAR** h_addr_list;
    } hostent;

     

     

    The gethostbyaddr function retrieves the host information corresponding to a network address.

    Note  The gethostbyaddr function has been deprecated by the introduction of thegetnameinfo function. Developers creating Windows Sockets 2 applications are urged to use the getnameinfo function instead of the gethostbyaddr function. See Remarks.

    struct HOSTENT* FAR gethostbyaddr(
      const char* addr,
      int len,
      int type
    );

    The gethostbyname function retrieves host information corresponding to a host name from a host database.

    Note  The gethostbyname function has been deprecated by the introduction of thegetaddrinfo function. Developers creating Windows Sockets 2 applications are urged to use the getaddrinfo function instead of gethostbyname.

    struct hostent* FAR gethostbyname(
      const char* name
    );

     

     

    异步聊天代码:

     

    添加库文件

     

    #include <winsock2.h>

     

    BOOL CChatApp::InitInstance()

    {

    WORD wVersionRequested;

    WSADATA wsaData;

    int err;

     

    wVersionRequested = MAKEWORD( 2, 2 );

     

    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 ) {

     

    return FALSE;

    }

     

     

    if ( LOBYTE( wsaData.wVersion ) != 2 ||

            HIBYTE( wsaData.wVersion ) != 2 ) {

     

    WSACleanup( );

    return FALSE;

    }

     

    …...

    }

     

    在实例的析构中使用:

    WSACleanup();

     

    定义消息

    #define UM_SOCK                WM_USER+1

     

    定义变量

    private:

    SOCKET m_socket;

     

    m_socket=0;

     

    if(m_socket)

    closesocket(m_socket);

     

    初始化套接字

    BOOL CChatDlg::InitSocket()

    {

    m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);

    if(INVALID_SOCKET==m_socket)

    {

    MessageBox("创建套接字失败!");

    return FALSE;

    }

    SOCKADDR_IN addrSock;

    addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

    addrSock.sin_family=AF_INET;

    addrSock.sin_port=htons(6000);

    if(SOCKET_ERROR==bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR)))

    {

    MessageBox("绑定失败!");

    return FALSE;

    }

    if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))

    {

    MessageBox("注册网络读取事件失败!");

    return FALSE;

    }

     

    return TRUE;

    }

     

    接受数据的消息处理

    afx_msg void OnSock(WPARAM,LPARAM);

     

    ON_MESSAGE(UM_SOCK,OnSock)

     

    void CChatDlg::OnSock(WPARAM wParam,LPARAM lParam)

    {

    switch(LOWORD(lParam))

    {

    case FD_READ:

    WSABUF wsabuf;

    wsabuf.buf=new char[200];

    wsabuf.len=200;

    DWORD dwRead;

    DWORD dwFlag=0;

     

    SOCKADDR_IN addrFrom;

    int len=sizeof(SOCKADDR);

    CString str;

    CString strTemp;

    HOSTENT *pHost;

    if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,

    (SOCKADDR*)&addrFrom,&len,NULL,NULL))

    {

    MessageBox("接收数据失败!");

    return;

    }

    pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);

    //str.Format("%s说 :%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);

    str.Format("%s说 :%s",pHost->h_name,wsabuf.buf);

    str+="/r/n";

    GetDlgItemText(IDC_EDIT_RECV,strTemp);

    str+=strTemp;

    SetDlgItemText(IDC_EDIT_RECV,str);

    break;

    }

    }

     

    发送数据

    void CChatDlg::OnBtnSend()

    {

    // TODO: Add your control notification handler code here

    DWORD dwIP;

    CString strSend;

    WSABUF wsabuf;

    DWORD dwSend;

    int len;

    CString strHostName;

    SOCKADDR_IN addrTo;

    HOSTENT* pHost;

    if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="")

    {

    ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

    addrTo.sin_addr.S_un.S_addr=htonl(dwIP);

    }

    else

    {

    pHost=gethostbyname(strHostName);

    addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]);

    }

     

    addrTo.sin_family=AF_INET;

    addrTo.sin_port=htons(6000);

     

    GetDlgItemText(IDC_EDIT_SEND,strSend);

    len=strSend.GetLength();

    wsabuf.buf=strSend.GetBuffer(len);

    wsabuf.len=len+1;

     

    SetDlgItemText(IDC_EDIT_SEND,"");

     

    if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0,

    (SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))

    {

    MessageBox("发送数据失败!");

    return;

    }

     

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值