简体   繁体   中英

Should mutex be used when inserting element in vectors?

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.)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM