The strongest-guarantee solution is to lock the entire vector on all such operations (which means controlling every operation from everywhere in the code, which really means creating a synchronised vector).
It may be that something as simple as this will do for your purposes:
int append(std::vector<int>& numbers, int number){
int retval = numbers.size();
// what if some other thread calls push_back(number) in between these calls?
numbers.push_back(number);
int newSize = numbers.size();
//this bit is as a short-cut in common, easy, cases
if(newSize = retval + 1) //no need for further complication
return retval;
while(++retval < newSize)
if(numbers[retval] == number)
return retval;
//If we get this far, numbers have been deleted, not added. More discussion below.
}
One thing about this is that if threads push 3, 3, 3, 3 then the index returned will be wrong, though it will still be an index to a 3. Whether that's okay or not depends on your purposes.
Another is that if the vector is popped or otherwise shortened in the meantime, then at best we get to the point where I just put a comment in the code above, at worse it errors (as they pop again after we obtain newSize, and then accessing [retval] becomes invalid). You need to consider if this case can happen (maybe you know from the rest of the code that it never will) and what to do if it does.
If the limitations of this are too great for your use case, then producing a fully synchronised vector is the best I can think of I'm afraid.