两个线程各自获取自己的映射存储对象副本

时间:2011-12-20 01:36:56

标签: c++ multithreading sockets boost

标题说明了一切,我认为将对象存储在对象容器中将允许简单的跨线程类成员访问,因为它实际上将对象存储在由对象容器管理的内存空间中,在此案例一张地图。这是不正确的?因为发生了以下情况;

客户类:

class Client
{
public:
    Client(std::string clientID,SOCKET sock,bool quit);
    boost::thread_group *group;
    /*CUT*/
    std::string clientID;
    std::deque<std::string> snapShotsQueue;
    SOCKET sock;
    bool quit;
    void sendMessage(std::string);
    void threadSend(Client *client);
    void checksnapshots();
    /*CUT*/
};

地图

typedef std::map<std::string, Client> clientMap;
clientMap clientmap;
  1. 服务器启动
  2. 启动Boost线程(1)不断检查值。我们的想法是,如果服务器上发生某些事情,所有符合条件的客户都会收到通知。为此,将消息添加到客户端类的deque。
  3. 服务器不断接受新的客户端连接,每个连接都有自己的线程(ClientThread)。
  4. 在该线程(2)
  5. 内创建客户端对象
  6. 客户端类'构造启动另一个线程,该线程不断发送存储在客户端类对象的deque(3)中的消息。
  7. (1)在main()

    中创建的boost线程
    void alwaysWatching()
    {
        while(1)
        {
            /*CUT*/
            /* When something that needs to be communcated happens, a message will be formed and stored in the string "thaString" and sent to, in this case, all clients*/  
            for (clientMap::iterator it2 = clientmap.begin(); it2 != clientmap.end(); ++it2)
            {
                    it2->second.snapShotsQueue.push_back(thaString); //Add to client's deque
                    //Check how many items are in deque
                    std::cout << "There are now ";
                    it2->second.checksnapshots();                           
                    std::cout << "Snapshots waiting according to outside watcher" << std::endl;
            }
            /*CUT*/
        }
    }
    

    (2)创建客户端对象并将其添加到地图

    DWORD WINAPI ClientThread(LPVOID lpParam)
    {
        SOCKET        sock=(SOCKET)lpParam;
    
        /*CUT*/
    
        std::string clientID = "";
        std::stringstream ss;
        ss << lpParam; //Socket = clientID
        clientID = ss.str();
    
        Client client(clientID,sock,false); //Create object for this client
    
        while(1) //This thread is constantly waiting for messages sent by the client
        {
            /*CUT*/
            //Add clientID to map of clients
            if(clientAdded == false)
            {
                /*CUT*/
                clientmap.insert(std::pair<std::string,Client>(clientID,client));
                clientAdded = true;
                /*CUT*/
            }
            /*CUT*/
        return 0;
    }
    

    (3)将双端队列中的所有消息发送到客户端的线程

    //Struct used to create the thread that will keep on sending messages in deque to the client
    struct messageSender
    {
        messageSender(Client *client) : client(client) { }
    
        void operator()()
        {
            client->threadSend(client);
        }
        Client *client;
    };
    
    //Client constructor
    Client::Client(std::string clientIDs,SOCKET socks,bool quits)
    {
        /*CUT*/
        this->group = new boost::thread_group; //Create boost thread group (for later, possibly)
        messageSender startit(this); //Prep new thread for sending snapshot updates
        group->create_thread(startit); //Start new thread for snapshot updates
        /*CUT*/
    }
    
    //The actual function that constantly loops through the deque
    void Client::threadSend(Client *client)
    {
        /*CUT*/
        while(1)
        {
            /*CUT*/
                std::cout << "There are now ";
                client->checksnapshots();
                std::cout << "Snapshots waiting according to class thread queue processor" << std::endl;
            /*CUT*/
    
            unsigned int i;
            for(i=0; i < client->snapShotsQueue.size(); i++)
            {
                std::string theString;
                theString = client->snapShotsQueue.front();  // this gets the front of the deque
                client->snapShotsQueue.pop_front();             // this removes the front of the deque
    
                std::cout << "sending: " << theString << std::endl;
                client->sendMessage(theString);
            }
        }
    }
    

    正如您所看到的,我添加了一段代码,用于计算类外部线程以及类内部的双端队列。它们都报告不同的计数器,并且不会发送来自该类外部线程的消息。

    enter image description here

    所以看起来观察者线程(1)有自己的Client对象实例,即使它存储在地图内部。或者朝那个方向发展。

    我可能在指针方面做错了。有什么想法吗?

2 个答案:

答案 0 :(得分:4)

您正在将Client复制到地图中,是的,但是无论何时读出它们,您都会通过复制地图中的Client隐式创建新的std::map<std::string, Client *>。新副本将具有单独的快照队列。

您可能希望使用std::map<std::string *, Client *>Client,并使用new Client(...)(以及相应的delete s)分配您的所有Client。然后,对于您放入地图的每个客户端,可能只有一个{{1}}实例,其中包含多个指针副本。

答案 1 :(得分:1)

根据您的问题,您可能真的想在地图中存储指针而不是像Ruakh建议的那样 - 只需要小心或者使用shared_ptrs。

或者,如果您将对象存储在地图本身中就可以了,您可以通过引用访问它 - 在这种情况下,不会创建副本,但您不必处理内存分配:

简单示例:

std::map<std::string, int> m;
m["Test"] = 5;
int& val = m["Test"];
int& val2 = m["Test"];
val = 10;
printf("%d\n", val2); // prints 10, not 5.