Каждый из двух потоков получает свою собственную копию объекта, хранящегося в карте. - PullRequest
2 голосов
/ 20 декабря 2011

Заголовок вроде говорит сам за себя, я думал, что хранение объекта в контейнере объекта позволит легко получить перекрестный доступ к элементу класса, потому что он по существу хранит объект в памяти, которая управляется контейнером объекта, в этом бывает карта. Это неверно? Потому что происходит следующее:

Класс клиента:

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. Запущен вспомогательный поток (1), который постоянно проверяет значения. Идея заключается в том, что если на сервере происходят определенные события, все соответствующие клиенты получают уведомление. Чтобы это произошло, сообщение добавляется в очередь клиентского класса.
  3. Сервер постоянно принимает новые клиентские соединения, каждый из которых получает свой собственный поток (ClientThread).
  4. В этом потоке создается клиентский объект (2)
  5. Конструкция класса клиента запускает еще один поток, который постоянно отправляет сообщения, хранящиеся в deque (3) объекта класса клиента.

(1) Поток поддержки, созданный в main ()

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);
        }
    }
}

Как вы можете видеть, я добавил фрагмент кода, который считает deque как в потоке вне класса, так и внутри класса. Они оба сообщают о различных счетчиках, и сообщения из потока за пределами класса не отправляются.

enter image description here

Таким образом, похоже, что поток наблюдателя (1) имеет свой собственный экземпляр объекта Client, даже если он хранится внутри карты. Или что-то в этом направлении.

Я, вероятно, делаю что-то не так с указателем. Есть идеи?

Ответы [ 2 ]

4 голосов
/ 20 декабря 2011

Вы копируете Client s на карту, да, но затем, когда вы их считываете, вы неявно создаете новые Client s, копируя их на карту.Новые копии будут иметь отдельные очереди снимков.

Возможно, вы захотите использовать std::map<std::string, Client *> или std::map<std::string *, Client *> и распределить все свои Client с помощью new Client(...) (с соответствующим deleteс).Затем для каждого клиента, помещенного на карту, может быть только один экземпляр Client с несколькими копиями указателей на него.

1 голос
/ 20 декабря 2011

В зависимости от вашей проблемы, вы, возможно, захотите хранить указатели вместо объектов на карте, как предлагает 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.
...