The container can be accessed without any locking mechanism, only if all the threads involved are reader threads.
The thread safety of stl containers has been discussed here:
Why does the C++ STL not provide a set of thread-safe containers?
Quoting the spec:
23.2.2 Container data races
"implementations are required to avoid data races when the contents of
the contained object in different elements in the same container,
excepting vector, are modified concurrently."
In short, in your case, since both insert and find are involved from different threads, locking is needed.
Use-case where locking is needed:
If you have a datastructure on which insert and find are being performed intermittently/concurrently, then locking is needed .
Use-case where locking is not needed:
If you have a datastructure that is populated at one shot and then subsequently, only finds are performed then locking is not needed.
Here is the source code:
STL map uses rb-tree internally.so, here is a look at rb-tree find method.
template <class _Key, class _Value, class _KeyOfValue,
class _Compare, class _Alloc>
typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator
_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::find(const _Key& __k)
{
_Link_type __y = _M_header; // Last node which is not less than __k.
_Link_type __x = _M_root(); // Current node.
while (__x != 0)
if (!_M_key_compare(_S_key(__x), __k))
__y = __x, __x = _S_left(__x);
else
__x = _S_right(__x);
iterator __j = iterator(__y);
return (__j == end() || _M_key_compare(__k, _S_key(__j._M_node))) ?
end() : __j;
}
As seen, there is no lock used and it makes sense since the overhead of lock is not really needed/desired for non-threaded applications.