问题描述:(网络编程 WINSOCKET TCP)用户A登录系统,建立线程A,登录成功后,系统建立用户A的客户端列表,并改变用户A的连接状态为已连接。此时连接线程保持。之后,用户A在异地登再次录系统,建立线程B,系统检测用户A先前有连接,就关闭它,并等待先前的线程退出。在先前线程就是线程A退出之后,重新建立用户A的客户端列表。线程A退出时,需要释放资源,删除客户端列表信息,改变连接状态等。问题发生在这个时候:用户再次登录系统时线程B通过互斥锁访问客户端列表,当发现存在此用户连接时,等待线程A退出,线程A退出后,重建客户列表,很明显在互斥未释放时,等待线程A退出,要发生死锁。请问,有什么办法解决这样的问题吗?

解决方案 »

  1.   

    问题代码:
    线程B:
        进入互斥区间
        waitforsingleobject()等待线程A退出
        离开互斥区间线程A:
        进入互斥区间
        释放资源
        离开互斥区间这样,当线程B进入互斥区间后,等待线程A退出时,线程A无法进入互斥区间,所以也无法退出,导致线程B和线程A死锁。
      

  2.   

    可以在线程B中先在互斥中检测客户列表的状态,如果存在则离开互斥,等待事件。触发事件后再进入互斥更新客户列表。不然,这种方式
    问题代码:
    线程B:
      进入互斥区间
      waitforsingleobject()等待线程A退出
      离开互斥区间线程A:
      进入互斥区间
      释放资源
      离开互斥区间
    线程A肯定进入不了互斥区间。
      

  3.   


    因为A不退出,B无法重建客户端列表。在A没退出的情况下,B重新建立客户端列表,那么当线程A退出的时候,可能会把新建立的客户端列表给删除掉。
      

  4.   


    就是1个用户,系统只能保存一份有关此用户的客户端列表。用户第一次登录时,系统建立一个次用户ID唯一的客户端列表,等线程(假如是线程A)退出时,删除此客户端列表。
    等用户再次登录时,系统需要检查此用户之前是否存在连接,如果存在,强制之前的连接(线程A)下线,再线程A下线之后,重建用户列表。
      

  5.   

    你可以再弄个event,当线程A结束后,setevent.
      

  6.   

    waitforsingleobject()又没有访问临界资源
    为啥要加锁呢
      

  7.   

    你说的还是有问题啊
    照你这么说那A首次连接的时候列表哪来的?
    如果A可以正常实现连接那再次有连接申请的时候完成可以先处理掉A再从头处理B啊
      

  8.   

    楼上的方法,是用户再此登录拒绝登录,我是要强制前一个登录下线,为此次请求建立连接。我已经用这种方法解决了,但还是存在一个小问题,就是用户退出之后,下面这个结构体指针资源不能释放,只有再用户再次登录的时候,才能释放上次连接的资源。typedef struct _THREAD_INFO
    {
        SOCKET s; //客户端SOCKET句柄
        HANDLE hRecvThread; //客户端线程句柄
    }THREAD_INFO;THREAD_INFO* pThreadInfo = new THREAD_INFO;
    pThreadInfo->s = s; //监听得到的客户端SOCKET句柄
    pThreadInfo->hRecvThread = hRecvThread; //为客户端创建的数据接收线程句柄//接收数据时,连接登录命令处理
    case 'L': //连接握手命令
    {
        //如果之前存在此设备的连接线程,就关闭它
         //资源清理工作
         if (m_Map_ThreadListLock.AcquireWriteLock())
        {
    map<string,THREAD_INFO*>::iterator pItor;
    pItor = m_Map_ThreadList.find(sDeviceNO);
    if (pItor != m_Map_ThreadList.end())
    {
        closesocket(pItor->second->s);
        //等待线程退出
        if (WAIT_OBJECT_0 == WaitForSingleObject(pItor->second->hRecvThread,INFINITE))
       {
    CloseHandle(pItor->second->hRecvThread);
    pItor->second->hRecvThread = NULL;
    delete pItor->second;
    pItor->second = NULL;
       }
    }
    m_Map_ThreadList[sDeviceNO] = pThreadInfo;
    m_Map_ThreadListLock.ReleaseWriteLock();
        }    //.....建立客户端列表
         .................    //完成连接
    }
    break;
      

  9.   

    请问你是如何判断THREAD_INFO*没有释放的?
      

  10.   

    理解。要完成需求,可以这样:
    线程B:
      进入互斥区间
      检查并设置本地标志
      离开互斥区间
      通知A退出。
      如果本地标志:waitforsingleobject()等待线程A退出
      分配资源
    线程A:
      进入互斥区间
      释放资源
      离开互斥区间
      

  11.   

    我代码结构是这样的://数据接收线程
    UINT Recvthread(PVOID pVoid)
    {
        TRHEADPARAM* pThreadParam = (TRHEADPARAM*)pVoid;    THREAD_INFO* pThreadInfo = new THREAD_INFO;
        pThreadInfo->s = pThreadParam->s; //监听得到的客户端SOCKET句柄
         pThreadInfo->hRecvThread = pThreadParam->hRecvThread; //为客户端创建的数据接收线程句柄
         
         //.......部分代码省略
         while(TRUE)
        {
             //接收数据
             int iRet = recv(ClientSocket,sz_RecvBuff,sizeof(sz_RecvBuff),0);
            if (iRet > 0 )
            {
                //处理出具
                  Run(pThreadInfo , sz_RecvBuff, iRet);
            }
            else
               break;
         }        //删除客户端列表
          ..........
        
        //释放线程参数
        delete pThreadParam;
        pThreadParam = NULL;
    }void Run(THREAD_INFO* pInfo, char* szBuff, int ilen)
    {
        BYTE nOrder = szBuff[...]; //得到命令标识
        //接收数据时,连接登录命令处理
        case 'L': //连接握手命令
        {
           //如果之前存在此设备的连接线程,就关闭它
            //资源清理工作
            if (m_Map_ThreadListLock.AcquireWriteLock())
           {
              map<string,THREAD_INFO*>::iterator pItor;
              pItor = m_Map_ThreadList.find(sDeviceNO);
              if (pItor != m_Map_ThreadList.end())
              {
                  closesocket(pItor->second->s);
                  //等待线程退出
                    if (WAIT_OBJECT_0 == WaitForSingleObject(pItor->second->hRecvThread,INFINITE))
                  {
                     CloseHandle(pItor->second->hRecvThread);
                     pItor->second->hRecvThread = NULL;
                     delete pItor->second;
                     pItor->second = NULL;
                  }
               }
               m_Map_ThreadList[sDeviceNO] = pThreadInfo;
               m_Map_ThreadListLock.ReleaseWriteLock();
            }        //.....建立客户端列表
              .................        //完成连接
         }
        break;
    }
      

  12.   


    兄弟,我有几个疑问:
    线程B:
      进入互斥区间
      检查并设置本地标志   //这个检查并设置本地标志,具体是做什么,这个标志是什么意思?
      离开互斥区间
      通知A退出。
      如果本地标志:waitforsingleobject()等待线程A退出
      分配资源这个思路,是否考虑到多线程并发的问题,就是当用户A,同时在两个或更多电脑上登录的情况?
      

  13.   


    引用jxingcn
    “我已经用这种方法解决了,但还是存在一个小问题,就是用户退出之后,下面这个结构体指针资源不能释放,只有再用户再次登录的时候,才能释放上次连接的资源”可以考虑搞个资源释放线程,每当这个线程要退出时,加入清理队列。
    在清理函数中等待这个线程结束,清理资源。
      

  14.   

    核心就是你的wait在离开criticalsection之后。标志用来记住是否有同一用户的其他线程需要等待。如果你愿意也可以记住其他所有的线程一起wait。
      

  15.   


    你这样会存在一个问题:
    用户A,在两个地方同时登录:
    线程A:                                   线程B:
    进入互斥区                                进入互斥区
    检查无等待线程标识                        检查无等待线程标识   
    离开互斥区                                离开互斥区
                      此时线程切换到线程B:
    进入互斥区                                进入互斥区
    建立客户端列表                            建立客户端列表
    离开互斥区                                离开互斥区这样,建立起的客户端列表就不正确了,可能是B线程的,也可能是A线程的
      

  16.   

    我上面的代码已经解决了这个问题,但就是THREAD_INFO*释放有些问题,只有在用户再次登录的情况下,才能释放上一次连接产生的THREAD_INFO*