I know that need mutex when try to delete an element from a vector.
so, I wrote a sample code to check this.
class Test
{
public:
Test(int idx) : m_index(idx) {}
int m_index = { -1 };
int m_count = { 0 };
};
std::vector<std::unique_ptr<Test>> m_vec;
std::mutex m_mutex;
void foo1() // print element data
{
while (true)
{
std::unique_lock ulock(m_mutex);
for (auto& e : m_vec)
{
e->m_count ;
printf("%d : Count : %d\n", e->m_index, e->m_count);
}
ulock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
void foo2() // Only insert element
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(0, 99);
while (true)
{
int t = dis(gen);
if (t >= 0 && t < 10)
{
//std::unique_lock ulock(m_mutex);
m_vec.push_back(std::make_unique<Test>(m_vec.size()));
}
std::this_thread::sleep_for(std::chrono::milliseconds(t));
}
}
void foo3() // Only remove element
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(0, 99);
while (true)
{
int t = dis(gen);
if (t >= 0 && t < 10)
{
std::unique_lock ulock(m_mutex);
if (m_vec.empty() == false)
m_vec.erase(m_vec.begin());
}
std::this_thread::sleep_for(std::chrono::milliseconds(t));
}
}
int main()
{
m_vec.push_back(std::make_unique<Test>(1));
m_vec.push_back(std::make_unique<Test>(2));
m_vec.push_back(std::make_unique<Test>(3));
std::thread t1(foo1);
std::thread t2(foo2);
std::thread t3(foo3);
t1.join();
t2.join();
t3.join();
return 0;
}
If I proceed with erase() without using mutex, segment fault almost immediately occurred.
So I used mutex for the erase() routine, which seemed to work normally.
About 10 minutes later, however, a nullptr exception occurred when referring to e in the foo1() function.
Q1. push_back inserts data at the end. But why does the NULL error occur at the middle point? ex. vector size: 521, error index: 129)
Q2. When using ordered containers such as vector and deque, do I need mutex in insert functions?
Q3. What about Unordered containers? (like, unorderd_map) (Remove mutex from insert and operate without any problem for about 20 minutes.)
CodePudding user response:
foo2()
is accessing/modifying the vector
outside of the mutex
lock. As such, foo1()
and/or foo3()
(which do use the mutex
) are able to modify the vector
at the same time as foo2()
. That is undefined behavior.
When push_back inserts data at the end, why does the middle point (ex. vector size: 521, error index: 129) Null point error occur?
Pushing a new element into a vector
may require it to reallocate its internal array, thus moving all of the existing elements to a new memory block. You are doing that in foo2()
without the protection of the mutex
lock, so it is possible that the elements which foo1()
and foo3()
are accessing may disappear behind their backs unexpectedly.
Erasing elements from a vector
will not reallocate the internal array, but it may still shift elements around within the array's existing memory.
When using ordered containers such as vector and deque, do I need mutex in insert and delete functions?
Yes. All standard containers are not thread-safe, so modifications must be serialized when used across thread boundaries.
What about Unordered containers? (like, unorderd_map) (Remove mutex from insert and operate without any problem for about 20 minutes.)
Same thing.