服务端程序线程运行信息管理器

服务端程序线程运行信息管理器

 

 

现在的服务端程序都是多线程的并且其中有大量的工作现场,如何知道每一个工作线程都在干什么?

在服务端程序实现时必须考虑到一些工作需要耗时较长,甚至可能因为死锁等原因导致一些线程进入了某个调用就退步出来的情况,这个时候增加远程连接对服务端程序内部工作状态的查看功能就必不可少了;

本文基于一个服务端程序的线程运行信息管理的实现介绍原理并给出代码

首先我们要考虑到对线程运行信息的访问不能有频繁的锁操作,其次该信息应该能够根据使用情况自动的为每个需要运行信息的线程分配的存储空间并且各不关联,同时需要所有线程的运行信息能够进行遍历,这样就可以考虑这样实现:

  1. 线程的运行信息存储在线程专用存储中,这样不但数据各个线程分开避免了锁的开销,而且方便编写程序(就像使用一个单独的成员变量一样);
  2. 每一个线程的运行信息结构的指针和线程的ID关联起来放置到哈希映射表中,这样便于对所有使用了运行信息的线程进行遍历;
  3. 由于运行信息记录的是一个工作线程的工作过程,因此是一个开始工作时记录,结束工作时清除的类似栈的数据信息;

同时因为运行信息只记录需要时间较长并且可能引起无法返回的工作,所以信息量不会很大,因此使用使用一个固定大小的结构定义出来,然后使用 ACE_TSS 模板来自动进行线程专用存储的管理,同时使用 ACE_Hash_Map_Manager 来关联线程ID和运行信息指针;

下面是实现:

复制代码

#pragma  once

// ==================================================================================================
//     概述:
//         线程状态管理器:管理各个线程的运行状态
// ==================================================================================================
struct  StatusManager
{
public :
    
// ----------------------------------------------------------------------------------------------
    
//     概述:
    
//         设置线程的运行信息
    
// ----------------------------------------------------------------------------------------------
     static  ACE_UINT32 SetRunInfo(  const   char   *  RunInfo )
    {
        TASK_RUN_INFO 
&  TaskRunInfo  =  GetInstance( ).GetTaskRunInfo( );
        CHK_EXP_RUN( TaskRunInfo.Number 
>=  MAX_TRI_NUMBER , MAX_TRI_NUMBER );

        TASK_RUN_STATUS 
&  Status  =  TaskRunInfo.Status[ TaskRunInfo.Number ];
        strncpy( Status.String , RunInfo , MAX_TRI_LENGTH 
-   1  );
        ACE_OS::gettimeofday( ).msec( Status.DateTime );
        
return  TaskRunInfo.Number  ++  ;
    }

    
// ----------------------------------------------------------------------------------------------
    
//     概述:
    
//         清除线程的运行信息
    
// ----------------------------------------------------------------------------------------------
     static  ACE_UINT32 ClrRunInfo( ACE_UINT32 RunInfoID )
    {
        TASK_RUN_INFO 
&  TaskRunInfo  =  GetInstance( ).GetTaskRunInfo( );
        CHK_EXP_RUN( TaskRunInfo.Number 
<=  RunInfoID , RunInfoID );
        
        
//     消除指定信息及其以后的信息
         return  TaskRunInfo.Number  =  RunInfoID ;
    }

protected :
    
// ----------------------------------------------------------------------------------------------
    
//     概述:
    
//         定义数据结构和哈希映射类型
    
// ----------------------------------------------------------------------------------------------
     enum  TASK_RUN_INFO_CAPACITY { MAX_TRI_NUMBER  =   255  , MAX_TRI_LENGTH  =   120  };
    
struct  TASK_RUN_STATUS { ACE_UINT64 DateTime;  char  String[ MAX_TRI_LENGTH ]; };
    
struct  TASK_RUN_INFO { ACE_UINT32 Number; TASK_RUN_STATUS Status[ MAX_TRI_NUMBER ]; };
    
struct  S_TASK_RUN_INFO :  public  TASK_RUN_INFO { S_TASK_RUN_INFO( ) { memset(  this  ,  0  , sizeof *   this  ) ); } };
    typedef ACE_Hash_Map_Manager
<  ACE_thread_t , TASK_RUN_INFO  *  , ACE_Thread_Mutex  >  ACE_Hash_Map_Task_Run_Info;

    
// ----------------------------------------------------------------------------------------------
    
//     概述:
    
//         获取线程运行信息结构引用
    
// ----------------------------------------------------------------------------------------------
    inline TASK_RUN_INFO  &  GetTaskRunInfo( )
    {
        ACE_thread_t Thread 
=  ACE_Thread_Manager::instance( ) -> thr_self( );
        S_TASK_RUN_INFO 
*  lpTaskRunInfo  =  TaskRunInfo;
        HashRunInfo.rebind( Thread , lpTaskRunInfo );
        
return   *  lpTaskRunInfo ;
    }

    
// ----------------------------------------------------------------------------------------------
    
//     概述:
    
//         获取状态管理器
    
// ----------------------------------------------------------------------------------------------
     static  StatusManager  &  GetInstance( )
    {
        
static  StatusManager sxStatusManager;
        
return  sxStatusManager;
    }

protected :
    
// ----------------------------------------------------------------------------------------------
    ACE_Hash_Map_Task_Run_Info HashRunInfo;
    ACE_TSS
<  S_TASK_RUN_INFO  >  TaskRunInfo;
};

// ==================================================================================================
//     概述:
//         任务运行信息自动设置清除类
// ==================================================================================================
struct  DPS_Task_Run_Info
{
    inline DPS_Task_Run_Info( 
const   char   *  Format ,  )
    {
        
char  RunInfo[  128  ]  = ""  }; 
        va_list args; va_start( args , Format );
        vsnprintf( RunInfo , 
120  , Format , args );
        m_nTaskRunInfo 
=  StatusManager::SetRunInfo( RunInfo );
    }

    inline 
~ DPS_Task_Run_Info( )
    {
        StatusManager::ClrRunInfo( m_nTaskRunInfo );
    }

    ACE_UINT32 m_nTaskRunInfo;
};

// ==================================================================================================
//     概述:
//         根据编译选项来控制是否需要运行时任务信息
// ==================================================================================================
#ifdef _DPS_DISABLE_TASK_RUN_INFO_
#    define TASK_RUN_INFO( Format ,  )
#    define TASK_RUN_INFO_EX( Name , Format ,  )
#else
#    define TASK_RUN_INFO( Format ,  ) DPS_Task_Run_Info xTaskRunInfo( Format , __VA_ARGS__ )
#    define TASK_RUN_INFO_EX( Name , Format ,  ) DPS_Task_Run_Info Name( Format , __VA_ARGS__ )
#endif
复制代码

 

由于宏中使用了可变参数,所以需要 VC 7.1 及其以上版本才能正常编译,如果不需要可变参数以及信息的格式化功能,那么稍作修改即可使用在 VC6 上

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: QTcpServer多线程服务端是一种在Qt框架下实现的网络通信模式。它基于TCP协议,在服务端监听指定端口,并能够同时处理多个客户端的请求。 QTcpServer多线程服务端的实现主要包括两个步骤:线程管理和客户端连接管理。 线程管理:为了能够同时处理多个客户端的请求,我们可以通过创建多个线程来实现。在主线程中创建一个QTcpServer对象,然后通过调用其listen()函数指定监听的端口。当有客户端连接到服务时,QTcpServer会自动触发newConnection()信号。我们可以在槽函数中创建新的线程,并将新连接的套接字传递给线程的run()函数。 客户端连接管理:在每个线程中,我们可以通过接收套接字的读写信号来处理客户端的数据交互。在线程的run()函数中,可以创建一个QTcpSocket对象,并调用其setSocketDescriptor()函数将套接字的描述符传递给它。然后通过QIODevice的读写函数来接收客户端发来的数据,并根据协议进行相应的处理。同时,也可以通过调用QTcpSocket的write()函数将服务的响应发送给客户端。 需要注意的是,由于每个客户端连接都在独立的线程中处理,线程间的数据共享和同步需要特殊处理,以避免多线程并发访问的问题。我们可以使用互斥锁(Mutex)来保护共享资源,或者使用信号与槽机制来实现线程间的通信。 总结起来,QTcpServer多线程服务端能够同时处理多个客户端的请求,通过线程管理和客户端连接管理实现了服务与客户端的数据交互。这种模式在网络通信中具有广泛的应用,能够提高服务的并发处理能力和响应速度。 ### 回答2: Qt提供了一个名为QTcpServer的类,可以用来实现多线程的TCP服务端。使用多线程可以使得服务能够同时处理多个客户端连接请求,提高服务的并发处理能力。 在使用QTcpServer类之前,我们需要先创建一个继承自QTcpServer的自定义类,并重写其incomingConnection()函数。这个函数在每次有新的客户端连接时会被调用,我们可以在该函数中创建一个新的线程来处理这个客户端的请求,从而实现服务的多线程。 下面是一个简单的例子来说明如何使用QTcpServer和多线程来实现服务端: ```cpp #include <QtNetwork> class MyTcpServer : public QTcpServer { Q_OBJECT public: MyTcpServer(QObject *parent = nullptr) : QTcpServer(parent) {} protected: void incomingConnection(qintptr socketDescriptor) override { QThread *thread = new QThread; WorkerObject *worker = new WorkerObject(socketDescriptor); worker->moveToThread(thread); connect(thread, &QThread::finished, worker, &QObject::deleteLater); connect(worker, &WorkerObject::error, this, &MyTcpServer::workerError); connect(thread, &QThread::started, worker, &WorkerObject::process); thread->start(); } signals: void workerError(const QString &errorString); }; class WorkerObject : public QObject { Q_OBJECT public: WorkerObject(qintptr socketDescriptor, QObject *parent = nullptr) : QObject(parent) , m_socketDescriptor(socketDescriptor) {} public slots: void process() { QTcpSocket socket; if (!socket.setSocketDescriptor(m_socketDescriptor)) { emit error(socket.errorString()); return; } // 处理客户端的请求,例如读取数据或发送数据 socket.disconnectFromHost(); socket.waitForDisconnected(); } signals: void error(const QString &s); private: qintptr m_socketDescriptor; }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); MyTcpServer server; if (!server.listen(QHostAddress::Any, 1234)) { qDebug() << "Failed to start server!"; return -1; } return app.exec(); } #include "main.moc" ``` 在以上例子中,我们创建了一个名为MyTcpServer的自定义类,继承自QTcpServer,并重写了incomingConnection()函数。在incomingConnection()函数中,我们创建了一个新的线程,并创建了一个WorkerObject对象,将其移动到新创建的线程中。然后我们连接了一些信号和槽函数,使得当线程开始时,调用WorkerObject的process()函数来处理客户端的请求,当线程结束时,自动删除WorkerObject对象。另外,我们还连接了WorkerObject的error()信号与MyTcpServer的workerError()槽函数,以便在出错时能够捕获并处理错误。 最后,在main()函数中,我们创建了一个MyTcpServer的实例,并调用其listen()函数来开始监听指定的IP地址和端口号。如果监听失败,则会输出错误信息并退出程序。 这样,我们就实现了一个能够处理多个客户端连接的多线程TCP服务端。当有新的连接到来时,服务将为每个连接创建一个新的线程,从而实现了多个客户端的并发处理。 ### 回答3: Qt提供了一个名为QTcpServer的类,用于创建多线程服务端。下面是一个示例代码,说明如何使用QTcpServer创建多线程服务端。 ```cpp #include <QtNetwork/QTcpServer> #include <QtNetwork/QTcpSocket> #include <QtCore/QThread> // 自定义的处理客户端请求的线程类 class ClientThread : public QThread { Q_OBJECT public: explicit ClientThread(qintptr socketDescriptor, QObject *parent = nullptr) : QThread(parent), m_socketDescriptor(socketDescriptor) { } void run() override { QTcpSocket socket; if (!socket.setSocketDescriptor(m_socketDescriptor)) { emit error(socket.error()); return; } // 在这里处理客户端请求,比如接收和发送数据 // ... socket.disconnectFromHost(); if (socket.state() == QTcpSocket::ConnectedState) { socket.waitForDisconnected(); } } signals: void error(QTcpSocket::SocketError socketError); private: intptr_t m_socketDescriptor; }; // 主线程监听新的连接并创建处理请求的线程 class Server : public QObject { Q_OBJECT public: Server(QObject *parent = nullptr) : QObject(parent) {} public slots: void acceptConnection() { while (m_tcpServer->hasPendingConnections()) { QTcpSocket *clientSocket = m_tcpServer->nextPendingConnection(); // 创建线程并传递socket描述符 ClientThread *thread = new ClientThread(clientSocket->socketDescriptor(), this); connect(thread, &ClientThread::finished, thread, &ClientThread::deleteLater); thread->start(); } } void startServer() { m_tcpServer = new QTcpServer(this); // 监听指定端口 if (!m_tcpServer->listen(QHostAddress::Any, 1234)) { qDebug() << "Failed to start server"; return; } // 新连接到来时触发acceptConnection()槽函数 connect(m_tcpServer, &QTcpServer::newConnection, this, &Server::acceptConnection); } private: QTcpServer *m_tcpServer; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); // 创建服务端实例并启动服务 Server server; server.startServer(); return app.exec(); } ``` 在上面的例子中,新的连接到来时,服务端会创建一个新的线程来处理客户端请求。每个处理请求的线程都会独立运行,在自己的线程中与客户端进行数据传输。这种方式可以实现并发处理多个客户端的请求,提高了服务端的处理能力。 需要注意的是,在多线程环境下处理客户端请求时,需要保证线程安全,以免出现数据竞争等问题。可以使用互斥锁等机制来保证线程安全性。 希望上述回答能帮助到您,如有任何疑问,请随时追问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值