线程同步与异步套接字编程

线程同步与异步套接字编程
2009-12-26 21:44

1.用事件对象来实现线程同步

  • 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作

     

    事件对象和互斥对象一样都属于内核对象,它包含一个使用计数,一个用于标识该事件是一个自动

    重置还是一个人工重置的布尔值,和另一个用于指定该事件处于已通知状态还是未通知状态的布尔

    值。由上面所述,可见事件对象可分为两种,一种是人工重置的,另一种是自动重置的。

     

    当人工重置的事件得到通知时,等待该时间的所有线程均变为可调度线程,因为创建的人工重置对象 开始

    时处于有信号状态,除非手动 ResetEvent为无信号状态If TRUE, then you must use the ResetEvent

    function to manually reset the state to nonsignaled. )而当一个自动事件得到通知时,等待该时间的所有线程

    中只有一个线程变为可调度线程,因为当一个 线程申请到事件对象时,操作系统就会将它转换为无信号状

     

    用人工重置的方法无法完成线程同步:

    因为在申请事件对象的过程中Thread1和Thread2是按照时间片轮转执行的,他们都能申请到事件对 象,

    其后的代码都得到保护,两个线程同时访问一个资源,因此运行结果是未知的,同步失败

     

     

    下面是用自动重置的方法实现线程同步:

    #include<windows.h>

    #include<iostream.h>

     

    DWORD WINAPI Fun1Proc(LPVOID lpParameter);

    DWORD WINAPI Fun2Proc(LPVOID lpParameter);

     

    HANDLE g_hEvent;

     

    int tickets=100;

     

    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,NULL);//创建自动重置事件对象

    SetEvent(g_hEvent);//把它设置为有信号状态

     

    Sleep(4000);

     

    }

     

    DWORD WINAPI Fun1Proc(LPVOID lpParameter)

    {

     

     

    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)

    {

     

     

    while(TRUE)

    {

    WaitForSingleObject(g_hEvent,INFINITE);//保护下面的代码

    if(tickets>0)

    {

    Sleep(1);

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

    }

    else

    break;

    SetEvent(g_hEvent);

     

    }

    return 0;

    }

     

    2.通过创建命名事件对象来实现只允许单个程序运行

    g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");

    if(g_hEvent)

    {

    if(ERROR_ALREADY_EXISTS==GetLastError())

    {

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

    return;

    }

    }

     

    3.用临界区对象(关键代码段)实现线程同步

    CRITICAL_SECTION g_cs;//创建临界区对象

     

    InitializeCriticalSection(&g_cs);//初始化临界区对象

     

     

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

    。。。。。。。。。。//中间是被保护的代码

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

     

     

     

    DeleteCriticalSection(&g_cs);//删除临界区度喜爱那个

     

     

    4.线程死锁(线程同步过程中要避免的问题)

    哲学家进餐的问题

     

    线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A的拥 有权,

    就造成了死锁

     

     

     

     

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

     

    互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象 这样的内

    核对象,可以在多个进程中的各个线程间进行同步。

     

     

    关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在 等待进入

    关键代码段时无法设定超时值。

     

     

    实现线程同步时首选临界对象法,如果程序中要用到多个临界对象,就必

    须采用互斥对象和事件对象来避免死锁。

     

     

    深入了解多线程编程和线程同步推荐《Windows核心编程》

     

     

     

     

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

     

    同步(Sync)指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式,

    异步(Async)方式指的是发送方不等接收方响应,便接着发下个数据包的通信方式。

     

     

    阻塞套接字模式是指执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上,比如调用

    recv()函数读取网络缓冲区中的数据,如果没有数据到达,将一直挂在recv()这个函数调用上,直到读到一些数据,

    此函数调用才返回。

    非阻塞套接字模式是指执行此套接字的网络调用时,不管是否执行成功,都立即返回。比如调用recv()函数读

    取网络缓冲区中数据,不管是否读到数据都立即返回,而不会一直挂在此函数调用上。

     

    在实际Windows网络通信软件开发中,异步非阻塞套接字是用的最多的。平常所说的

    C/S(客户端/服务器)结构的软件就是异步非阻塞模式的。

     

     

    同步跟异步模式作用在客户端(发送请求消息一方),阻塞跟非阻塞模式作用在服务端

    (处理接收消息的一端)。异步非阻塞模式就是客户端可以不停发送请求,服务端可以

    边干自己的事边响应发过来的请求,客户端不必因为服务端没有回应而停止工作,服务

    端也不会因为没有收到请求而一直阻塞。这种模式的完成主要依靠WSAAsyncSelect()

    供的消息机制下的网络事件选择来实现的,当使用它登记的网络事件发生时(比如客户

    端向服务端发送了一条请求消息),Windows应用程序相应的窗口函数将收到一个消息

    (消息中指示了发生的网络事件,以及与事件相关的一些信息),然后依据消息内容对

    客户端进行响应。在这个过程中,服务端程序对客户端的响应变为基于消息的模式,而

    不是基于过程,这样就避免了服务器端的阻塞。(上一节编写的网络聊天程序是在阻塞

    模式下运行的,之所以没有发生阻塞,是因为应用程序采用了多线程技术,接收信息

    这个任务交给另外一个线程来完成,从而避免了主线程的阻塞

     

     

     

    当使用socket()WSASocket()函数创建套接字时,默认都是阻塞的,而WSAAsyncSelect()会

    将套接字设置为非阻塞模式。

     

     

     

     

    7.用异步套接字实现网络聊天程序

    BOOL CChatDlg::OnInitDialog()

    {

    InitSocket();

    }

     

    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;

    }

     

     

     

    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;

    }

     

    }

     

     

     

    8.Windows下编写高性能网络程序要注意的几点:

    Windows程序都是基于消息的,采用基于消息的异步选择机制可以增加网络程序的效率,Windows平

    台下要想编写高性能应用程序除了解网络之外,还必须了解程序在Windows下的工作原理。调用网络

    函数时要对返回值进行判断,编写网络程序时要仔细,多做实验多调试,只有这样才能编写出高性能

    的网络程序。

     

     

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值