0

I am using a map<int, queue<string>>, where int refers to the source of a message, and the queue holds the message. One thread pushes messages into the queue, another thread pushes them out of the queue.

This is a client-server program - when the client sends a message, the message gets pushed into the queue.

I am currently using (pseudo code)

/*receive message in thread 1*/
map<int, queue<string>> test_map; 
int client_id = 2;
string msg = received_from_client(client_id);
testmap[client_id].push(msg);

/*process message in thread 2*/
string msg_to_process testmap[client_id].front();
test_map[client_id].pop();

if (testmap[client_id].empty())
{
    testmap.erase(client_id);
}

I know from this question that the difference is that insert will not overwrite an existing key - does this apply when I am pushing things into queues? Is it safer to use insert, or is what I'm doing with [] sufficient?

Also - while the system should only have one message in the queue at any one time, I am making expansion allowances by using map<int, queue> instead of using map<int,string>.

edit: I have a question about multiple threading as well - what happens when thread 1 attempts to insert into the map while thread 2 deletes the key because the queue is empty (after it has processed the message). Is that a quantitative answer to this, and does using [] or insert() help make it anymore threadsafe?

Community
  • 1
  • 1
sccs
  • 1,123
  • 3
  • 14
  • 27
  • Apart from what is likely to be some pretty interesting concurrency issues once you introduce threading into this, your management of using `operator []` for indexing a specific client id's queue for insert, front, and pop, looks correct from here. You may wish to use smart pointers for your map value if you're planning on hitting this in a multi-threaded environment (for reasons you'll discover once you get there). – WhozCraig Apr 19 '13 at 03:36
  • I am actually asking this question because the program occasionally breaks - still checking for cause - I have another question about the insertion and deletion - I'll edit the question! – sccs Apr 19 '13 at 03:38
  • Then is your question how this code breaks in a multi-threaded environment? (does the original unspoken, undocumented, and therefore unknown error happen in a single-threaded context)? – WhozCraig Apr 19 '13 at 03:42
  • The error does not occur in a single-threaded context. My testing is in a multi-threaded environment (where the error occurs) - though it is not clear to me that it is this issue that breaks it necessarily (many other lines of code that could throw up a bug). So I suppose my question truly is how **would** this break in a multi-threaded environment? – sccs Apr 19 '13 at 04:55
  • 1
    Standard library containers are completely thread-safe in read-only-mode *only*. As soon as you introduce a writer (and in this case, both your value-queues and the map itself are volatile) all bets are off and you have unabated concurrency problems. It would not be terribly difficult to address this with a proper setup of appropriately placed lock conditions with the proper synchronization objects. Tedious, yes. Impossible no. at the same time you're erasing a member from the map, for example, you could be accessing or adding one on another thread. Likewise for the value queues. – WhozCraig Apr 19 '13 at 05:51
  • And to address your last question, not. using either make it no more thread-safe than the other. Standard lib containers are *not* out-of-the-box thread-safe with concurrent write operations against either other writes or other reads. Only exclusive reads need not latch the container in some fashion. – WhozCraig Apr 19 '13 at 06:36

1 Answers1

0

Queue's don't have keys or [] operators, so your first question can't really be answered. You insert into queue's by pushing onto the back. If there are elements there, it will go after them. You read off a queue by popping things off of the front, if there are any. You don't read or write anywhere other than that.

As for maps, like you said, insert will add a new key-value pair if it does not exist already. It will not overwrite an existing key. Find will find a value if it exists already, but will not insert it if it doesn't. And then the [] operator does both, and also allows you to change existing elements. The documentation here is very good.

One thing to be aware of is that using the map's [] operator to read from the map will also insert a default valuetype element into the map with that key, and is probably not what you would expect when first looking at it.

std::map<int, int> myMap;
if(myMap[1] == 0) //[] create's a key-value pair <1,0>
   cout << "This will output";
if(myMap.size() == 1)
   cout << "This too";

As for the thread safety aspect, no STL containers are thread safe based on the standard. You need to add proper locking in your code to prevent exactly what you asked about. If 2 threads tried to read and write from a queue at the same time, it will almost definitely cause an error. I would google around about writing thread safe programs for general help on how to do that.

bdwain
  • 1,665
  • 16
  • 35
  • Thanks - my question was always about `[]` and `insert` wrt to maps (that was a typo on my part, sorry). With regard to it inserting a default valuetype element - what is the solution to that? To use `find()->second`? – sccs Apr 22 '13 at 01:45
  • yes. if you want to search for something without inserting a default value, you need to use find, which will return an iterator to a std::pair, so you can call ->second on that to retrieve the value. you need to make sure the iterator doesn't equal mymap.end() before you use it though, because that's how it tells you a key wasn't found. There's good explanations and examples [here](http://www.cplusplus.com/reference/map/map/find/) – bdwain Apr 22 '13 at 02:39
  • so in your example, if you looked for a queue with id 7, and there was none, an empty queue would be created and put in the map. – bdwain Apr 22 '13 at 02:45
  • what if I did things like `if (!myMap[1].empty()){}` - would this cause the same problem? – sccs Apr 22 '13 at 06:17
  • I don't know what you mean by cause the same problem. If you're asking whether the if condition will evaluate to true, the answer is yes unless there was already a queue at id 1 and it was not empty. Whenever you use the subscript operator to do anything with a map, you're guaranteeing an object with that key will exist. The first thing it will do when you call [] on a key that doesn't exist is create an empty valuetype object and insert it. That's why i typically don't use the [] for searching a map ever – bdwain Apr 22 '13 at 19:30