Huseyn Rahimov
Huseyn Rahimov

Reputation: 71

Using Mutex and lock_guard with a vector in C++

I am a newbie in Threading in C++. I need your help regarding usage of mutex and lock_guard (this part doesn't matter). I have a main function and a secondary functions.

Please tell me why multithreading doesn't work when I add lock_guard(mtx); When I remove it, it runs faster but wrongly. Can you help me with it?

I need a correct access to the vector vec and enabling threading.

#include <vector>
#include <thread>

std::mutex mtx;

void threadCall(std::vector<int> &vec, int start, int end){
    std::lock_guard<std::mutex> guard(mtx);
    for(int i=start; i<end; i++)
      vec[i] = i;
}

void ThreadFunc(std::vector<int> vec){
    std::vector<std::thread> threads(2);
    threads[0] = std::thread(&threadCall, std::ref(vec), 0, 10);
    threads[1] = std::thread(&threadCall, std::ref(vec), 10, 20);

    threads[0].join();
    threads[1].join();
}

int main(){

    std::vector<int> vec(20);
    ThreadFunc(vec);

    return 0;
}

Upvotes: 1

Views: 7802

Answers (3)

To start with, stop passing the vector by value, pass references. After that is resolved, your particular example doesn't need a mutex at all, actually. Your vector has a fixed size:

std::vector<int> vec(20);

And all elements are default constructed from the get go. Since all you do is assignment:

vec[i] = i;

The vector won't reallocate any storage or adjust its item count. So there's no need to lock access to the vector as a whole. Couple that with the fact that each thread operates on a separate sub-range, and there aren't any data races present. You don't need synchronization primitives.

Upvotes: 4

Alex
Alex

Reputation: 10126

The problem is that mutex is taken by the first thread and second thread can't work until it's released.

So, essentially you receive a multi-threaded application that where all threads do their job serially.

You can move your guard into for-loop

for(int i=start; i<end; i++) {
  std::lock_guard<std::mutex> guard(mtx);
  vec[i] = i;
}

That will give threads a possibility to work together over the passed vector.

BUT:

You need to take into account that multiple threads not always give a performance boost jut because concurrency != parallelism.

I would expect from this application in this way be actually slower than a single-threaded implementation because the following:

  1. Threads are blocking each other by a mutex so only one thread runs at time
  2. You spend time on context switch between threads

SOLUTION IDEA:

If you want to run it truly parallel, you need to make threads work on independent data and then join the result.

Upvotes: 4

Chris Drew
Chris Drew

Reputation: 15334

The mutex is preventing the threads from doing any work in parallel. As long as you can guarantee that each thread is not going to write to the same part of the vector you don't need the mutex at all.

The other problem is you are passing your vector by value. You should instead pass by reference:

void threadCall(std::vector<int>& vec, int start, int end){
    for(int i=start; i<end; i++)
      vec[i] = i;
}

void ThreadFunc(std::vector<int>& vec){
    std::vector<std::thread> threads(2);
    threads[0] = std::thread(&threadCall, std::ref(vec), 0, 10);
    threads[1] = std::thread(&threadCall, std::ref(vec), 10, 20);

    threads[0].join();
    threads[1].join();
}

int main(){
    std::vector<int> vec(20);
    ThreadFunc(vec);
}

Live demo.

Upvotes: 5

Related Questions