why introducing sleep causes producer consumer to wait? - c++

I'm just learning to use the C++ threading library.
If anyone is curious - my code is just a modified version in a tutorial https://www.youtube.com/watch?v=13dFggo4t_I&t=6m45s
I wrote a simple producer/consumer code. I tried to introduce a sleep to make the producer and consumer in lockstep fashion, so item is consumed as soon as it is produced. But making the producer waits causes some deadlock. I couldn't figure out why.
Can you please help me point out what I'm missing in the code?
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <chrono>
#define BUFFER_SIZE 10
std::mutex mu;
std::condition_variable full,empty;
std::vector<int> vec;
int i=0;
std::chrono::milliseconds slp(10);
void produce()
{
while (i < 2*BUFFER_SIZE) {
std::unique_lock<std::mutex> locker(mu);
full.wait(locker, [] {return vec.size() != BUFFER_SIZE;});
vec.push_back(i++);
empty.notify_one();
//std::this_thread::sleep_for(slp); <--- introducing this causes program to hang.
}
}
void consume()
{
while(!vec.empty()) {
std::unique_lock<std::mutex> locker(mu);
empty.wait(locker, [] {return !vec.empty();});
std::cout << "Consumed:" << vec.back() <<"\n";
vec.pop_back();
full.notify_one();
}
}
int main() {
vec.reserve(BUFFER_SIZE*2);
std::thread producer(produce), consumer(consume);
producer.join();
consumer.join();
return 0;
}
EDIT:
void produce()
{
while (i < 2*BUFFER_SIZE) {
std::unique_lock<std::mutex> locker(mu);
full.wait(locker, [] {return vec.size() != BUFFER_SIZE;});
vec.push_back(i++);
locker.unlock();
empty.notify_one();
std::this_thread::sleep_for(slp);
}
}
void consume()
{
while(!vec.empty()) {
std::unique_lock<std::mutex> locker(mu);
empty.wait(locker, [] {return !vec.empty();});
std::cout << "Consumed:" << vec.back() <<"\n";
vec.pop_back();
locker.unlock();
full.notify_one();
}
}

You have a couple of problems:
The consumer is accessing vec without the mutex held:
while(!vec.empty()) {
This is easily addressed by ensuring that all acceses to vec are "inside" the mutex.
If the consumer gets ahead of the producer and manages to empty vec, it will exit early. You can fix this by using some other mechanism to indicate completion.
You are performing some processing/io/sleep with the mutex held, reducing possible concurrency. ideally you should only hold the mutex while accessing shared state.
const unsigned BUFFER_SIZE = 10;
std::mutex mu;
std::condition_variable full,empty;
std::vector<int> vec;
const std::chrono::milliseconds slp(10);
auto done = false;
void produce()
{
for (auto i = 0u; i < 2 * BUFFER_SIZE; ++i) {
std::unique_lock<std::mutex> locker(mu);
full.wait(locker, [] {return vec.size() < BUFFER_SIZE;});
auto was_empty = vec.empty();
vec.push_back(i);
locker.unlock();
// Only notify if the buffer was empty before the push_back
if (was_empty) {
empty.notify_all();
}
std::this_thread::sleep_for(slp);
}
}
void consume()
{
for (;;) {
std::unique_lock<std::mutex> locker(mu);
while (vec.empty()) {
if (done) {
return;
}
empty.wait(locker);
}
auto was_full = vec.size() >= BUFFER_SIZE;
auto value = vec.back();
vec.pop_back();
locker.unlock();
if (was_full) {
full.notify_all();
}
std::cout << "Consumed: " << value << '\n';
}
}
int main() {
vec.reserve(BUFFER_SIZE*2);
std::thread producer(produce), consumer(consume);
producer.join();
// Produce some more
producer = std::thread(produce);
producer.join();
// Produce A LOT more
std::vector<std::thread> many_producers(8);
for (auto&& t : many_producers) {
t = std::thread(produce);
}
for (auto && t : many_producers) {
t.join();
}
// Tell consumer we are done producing
{
std::lock_guard<std::mutex> lock(mu);
done = true;
}
empty.notify_one();
consumer.join();
}
See it live at Coliru.

Related

C++ multi-threading Error: Single producer Multiple consumer

I am trying to achieve Single producer Multiple consumer , however below code is not able to compile.
Can someone help with this error ? Also would it work to wake all threads from this pool & a random thread would be able to acquire the lock?
TIA
`
threadPool/main.cpp:4:
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/thread:364:17:
error: no matching constructor for initialization of '_Gp' (aka
'tuple<unique_ptrstd::__1::__thread_struct, void (TestClass::*)(),
TestClass>')
new _Gp(std::move(__tsp),
^ ~~~~~~~~~~~~~~~~~
ls/usr/bin/../include/c++/v1/type_traits:2422:12: error: call to implicitly-deleted copy constructor of 'typename
decay::type' (aka 'TestClass')
return _VSTD::forward<_Tp>(__t);
----------------------------------------------
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <condition_variable>
using namespace std;
class TestClass{
public:
void producer(int i) {
unique_lock<mutex> lockGuard(mtx);
Q.push(i);
lockGuard.unlock();
cond.notify_all();
}
void consumer() {
unique_lock<mutex> lockGuard(mtx);
cond.wait(lockGuard, [this]() {
return !Q.empty();
});
cout<<this_thread::get_id();
cout<<Q.front()<<endl;
Q.pop();
lockGuard.unlock();
};
private:
mutex mtx;
condition_variable cond;
queue<int> Q;
};
int main() {
std::cout << "Hello, World!" << std::endl;
int MAX_THREADS = std::thread::hardware_concurrency()-1;
vector<thread> ThreadVector;
TestClass testObj;
for(int i=0; i<MAX_THREADS; i++){
ThreadVector.emplace_back(&TestClass::consumer, std::move(testObj));
cout<<"Pool threadID:" <<ThreadVector[i].get_id()<<endl;
}
TestClass testObj2;
for(int i=0; i<10; i++) {
testObj.producer(i);
}
for(auto &&t : ThreadVector) {
t.join();
}
return 0;
}
`
Another version to call threads
int main()
{
std::vector<std::thread> vecOfThreads;
std::function<void(TestClass&)> func = [&](TestClass &obj) {
while(1) {
obj.consumer();
}
};
unsigned MAX_THREADS = std::thread::hardware_concurrency()-1;
TestClass obj;
for(int i=0; i<MAX_THREADS; i++) {
std::thread th1(func, std::ref(obj));
vecOfThreads.emplace_back(std::move(th1));
}
TestClass prod;
for(int i=0; i<10; i++) {
prod.producer(i);
}
for (std::thread & th : vecOfThreads)
{
if (th.joinable())
th.join();
}
return 0;
}
std::move(testObj) should be &testObj (a pointer to the object to call consumer on) - or std::ref(testobj) (which becomes a reference_wrapper (holding a pointer to the object too).
You should call produce at least as many times as you have threads, or else the program won't finish.
You don't need to unlock manually. The guards unlock automatically when they go out of scope.
Example:
class TestClass {
public:
void producer(int i) {
lock_guard<mutex> lockGuard(mtx); // here a lock_guard is enough
Q.push(i);
// no manual unlocking
cond.notify_all();
}
void consumer() {
unique_lock<mutex> lockGuard(mtx);
cond.wait(lockGuard, [this] { return !Q.empty(); });
cout << this_thread::get_id();
cout << Q.front() << endl;
Q.pop();
};
private:
mutex mtx;
condition_variable cond;
queue<int> Q;
};
int main() {
std::cout << "Hello, World!" << std::endl;
unsigned MAX_THREADS = std::thread::hardware_concurrency() - 1;
vector<thread> ThreadVector;
ThreadVector.reserve(MAX_THREADS); // since you know how many, reserve
TestClass testObj;
for(unsigned i = 0; i < MAX_THREADS; i++) {
// here, &testobj
ThreadVector.emplace_back(&TestClass::consumer, &testObj);
cout << "Pool threadID:" << ThreadVector[i].get_id() << endl;
}
// produce MAX_THREADS of things to put in the queue:
for(int i = 0; i < MAX_THREADS; i++) {
testObj.producer(i);
}
for(auto&& t : ThreadVector) {
t.join();
}
}
Regarding your questions in the comment section: If you'd like to keep the consumer threads running until you tell them to quit, you could add another variable (called run here) that the consumer threads monitor.
Example:
#include <condition_variable>
#include <iostream>
#include <queue>
#include <thread>
#include <vector>
using namespace std;
class TestClass {
public:
void producer(int i) {
lock_guard<mutex> lockGuard(mtx);
Q.push(i);
to_pool.notify_one();
}
void consumer() {
while(true) {
unique_lock<mutex> lockGuard(mtx);
to_pool.wait(lockGuard, [this] { return !run || !Q.empty(); });
if(!run) break; // time to quit
cout << this_thread::get_id() << ' ' << Q.front() << endl;
Q.pop();
// Tell producer that we picked one from the queue.
// if it's only interesting to notify when the queue is empty,
// add: if(Q.empty())
to_producer.notify_one();
}
};
void stop() {
lock_guard<mutex> lockGuard(mtx);
run = false; // tell all pool threads to quit
to_pool.notify_all();
}
void wait_for_all_work_to_be_done() {
std::unique_lock<mutex> lg(mtx);
to_producer.wait(lg, [this] { return Q.empty(); });
}
private:
bool run = true;
mutex mtx;
condition_variable to_pool;
condition_variable to_producer;
queue<int> Q;
};
int main() {
std::cout << "Hello, World!" << std::endl;
unsigned MAX_THREADS = std::thread::hardware_concurrency() - 1;
vector<thread> ThreadVector;
ThreadVector.reserve(MAX_THREADS);
TestClass testObj;
for(unsigned i = 0; i < MAX_THREADS; i++) {
ThreadVector.emplace_back(&TestClass::consumer, &testObj);
cout << "Pool threadID:" << ThreadVector[i].get_id() << endl;
}
for(int i = 0; i < MAX_THREADS / 2; i++) {
testObj.producer(i);
}
testObj.wait_for_all_work_to_be_done();
// stop pool threads
testObj.stop();
for(auto&& t : ThreadVector) t.join();
}

C++ Multiple consumer threads stuck on condition variable

I'm making a single producer, multiple consumers program in C++. I begin by calling consumer threads and then I add elements to an array.
Everything works fine, but in the end the consumer threads are not joining, because they're stuck waiting on condition variable and the program freezes.
I think the problem is that threads are constantly called in the loop because currentSize is not protected and they just can't exit out of the condition variable, but I don't know how to fix it.
struct Item {
public:
string name;
int time;
double height;
};
struct Monitor {
private:
Item items[12];
int currentSize;
bool finished;
mutex lock;
condition_variable cv;
public:
Monitor() {
finished = false;
currentSize = 0;
}
void put(Item item) {
unique_lock<mutex> guard(lock);
cv.wait(guard, [&] { return (currentSize < 12); });
items[currentSize] = item;
currentSize++;
cv.notify_all();
}
Item get() {
unique_lock<mutex> guard(lock);
cv.wait(guard, [&] { return (currentSize > 0); });
Item item = items[currentSize - 1];
currentSize--;
return item;
}
bool get_finished() {
return finished;
}
void set_finished() {
finished = true;
}
int get_size() {
return currentSize;
}
};
int main() {
vector<Item> items = read_file(file);
Monitor monitor;
vector<thread> threads;
vector<Item> results;
for (int i = 0; i < 4; i++) {
threads.emplace_back([&] {
while (!monitor.get_finished()) {
if (monitor.get_size() > 0) {
Item item = monitor.get();
results.push_back(item);
}
}
});
}
for (int i = 0; i < items.size(); i++) {
monitor.put(items[i]);
}
monitor.set_finished();
for_each(threads.begin(), threads.end(), mem_fn(&thread::join));
return 0;
}
Why the consumer threads block?
I have tested your code, and it turns out to be the producer thread blocking on the put() method. Why?
Imagine the following scenario: there are 13 items in the vector items.
The main thread (producer) happily loads the first 12 items, and waits on cv for the currentSize to become lower than 12.
The consumer threads are notified, and happily consume the first 12 items, and then wait on cv for currentSize to become greater than 0.
But wait! Now everyone is waiting on something, with no one notifying. Thus, all threads would block. You need to notify the producer when currentSize becomes lower than 12.
I noticed a few issues. made the member variables atomic, notify_all in get api. However there was al logic error as well. Imagine that you have 4 threads currently running and 5 items were in queue. At this point lets say each of the thread is able to get one out of the queue and now there are 4 threads and only one item in the queue. One of the thread takes the last one out and now there is 0 items in there however other three threads still waiting on the condition variable. So a solution is if the last item is out everythread should be notified and if there is no other elemnet get back from the API.
#include <iostream>
#include <vector>
#include <condition_variable>
#include <thread>
#include <algorithm>
#include <atomic>
using namespace std;
using Item = int;
struct Monitor {
private:
Item items[12];
std::atomic<int> currentSize;
std::atomic<bool> finished;
mutex lock;
condition_variable cv;
public:
Monitor() {
finished = false;
currentSize = 0;
}
void put(Item item) {
unique_lock<mutex> guard(lock);
cv.wait(guard, [&] { return (currentSize < 12); });
items[currentSize] = item;
currentSize++;
cv.notify_all();
std::cerr << "+ " << currentSize << std::endl ;
}
Item get() {
unique_lock<mutex> guard(lock);
cv.wait(guard, [&] { return (currentSize >= 0 ); });
Item item;
if (currentSize > 0 ){
currentSize--;
item = items[currentSize];
cv.notify_all();
std::cerr << "- " << currentSize << std::endl ;
}
return item;
}
bool get_finished() {
return finished;
}
void set_finished() {
finished = true;
}
int get_size() {
return currentSize;
}
};
int main() {
vector<Item> items(200);
std::fill ( items.begin() , items.end(), 100);
Monitor monitor;
vector<thread> threads;
vector<Item> results;
for (int i = 0; i < 10; i++) {
threads.emplace_back([&] {
while ( !monitor.get_finished() ) {
if (monitor.get_size() > 0) {
Item item = monitor.get();
results.push_back(item);
}
}
});
}
for (int i = 0; i < items.size(); i++) {
monitor.put(items[i]);
}
monitor.set_finished();
for_each(threads.begin(), threads.end(), mem_fn(&thread::join));
return 0;
}

How to implement semaphore? Is this implementation correct or faulty?

I want to implement semaphore class. And a user on stackoverflow has noted that my implementation is not working correct.
At the first I done it like this:
class sem_t {
int count;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
this->count++;
}
void down() {
while (this->count == 0)
std::this_thread::yield();
this->count--;
}
};
Then a user on stackoverflow noted that this implementation is faulty cause I read and write variable count out of any synchronization primitive and at some point the value can become incorrect and in case of compiler optimization compiler can assume that the variable count can not be modified by another thread. So, I tried to add mutex to this construction and I've done it like this:
class sem_t {
int count;
std::mutex mutualExclusion;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
this->mutualExclusion.lock();
this->count++;
this->mutualExclusion.unlock();
}
void down() {
this->mutualExclusion.lock();
while (this->count == 0)
std::this_thread::yield();
this->count--;
this->mutualExclusion.unlock();
}
};
But in case of using this approach when I try to detach thread i got an error saying that mutex has been destroyed while busy, cause one thread can hold mutex and then yield after which the thread is detached and error occurs (Is this solution ok?).
Then I tried to modify this code and I stoped on following construction:
class sem_t {
int count;
std::mutex mutualExclusion;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
this->mutualExclusion.lock();
this->count++;
this->mutualExclusion.unlock();
}
void down() {
while (this->count == 0)
std::this_thread::yield();
this->mutualExclusion.lock();
this->count--;
this->mutualExclusion.unlock();
}
};
But I think that this solution is faulty too, cause it can lead the same problem as the first solution.
So, whats the correct implementation?
I wanna note that I tried implementation with condition variable, but i am trying to implement semaphore without condition variable and if you want to suggest some solution with condition variable please describe how wait method of condition variable is working.
[Edit]
My full code, using self implemented semaphore:
#include "pch.h"
#include <iostream>
#include <vector>
#include <mutex>
#include <thread>
#include <chrono>
class sem_t {
int count;
std::mutex mutualExc;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
mutualExc.lock();
this->count++;
mutualExc.unlock();
}
void down() {
mutualExc.lock();
while (this->count == 0) {
mutualExc.unlock();
std::this_thread::yield();
mutualExc.lock();
}
this->count--;
mutualExc.unlock();
}
};
#define N 5
#define THINKING 0
#define HUNGRY 1
#define EATING 2
std::mutex mx;
std::mutex coutMX;
char philosopherState[N] = { THINKING };
sem_t philosopherSemaphores[N] = { 0 };
void testSetState(short i) {
if (philosopherState[i] == HUNGRY && philosopherState[(i + 1) % N] != EATING && philosopherState[(i + N - 1) % N] != EATING) {
philosopherState[i] = EATING;
philosopherSemaphores[i].up();
}
}
void take_forks(short i) {
::mx.lock();
philosopherState[i] = HUNGRY;
testSetState(i);
::mx.unlock();
philosopherSemaphores[i].down();
}
void put_forks(short i) {
::mx.lock();
philosopherState[i] = THINKING;
testSetState((i + 1) % N);
testSetState((i + N - 1) % N);
::mx.unlock();
}
void think(short p) {
for (short i = 0; i < 5; i++) {
coutMX.lock();
std::cout << "Philosopher N" << p << " is thinking!" << std::endl;
coutMX.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void eat(short p) {
for (short i = 0; i < 5; i++) {
coutMX.lock();
std::cout << "Philosopher N" << p << " is eating!" << std::endl;
coutMX.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void philosopher(short i) {
while (1) {
think(i);
take_forks(i);
eat(i);
put_forks(i);
}
}
int main()
{
std::vector<std::thread*> threadsVector;
for (int i = 0; i < N; i++) {
threadsVector.push_back(new std::thread([i]() { philosopher(i); }));
}
std::this_thread::sleep_for(std::chrono::milliseconds(15000));
for (int i = 0; i < N; i++) {
threadsVector[i]->detach();
}
return 0;
}
Error that is occurring (only occurs when running program in release or debug mode in visual studio)
The last attempt is indeed not correct because it may happend that several threads call down at the same time and all successfully pass
while (this->count == 0)
std::this_thread::yield();
lines and then they will all decrement the counter to possiby negative value:
this->mutualExclusion.lock();
this->count--;
this->mutualExclusion.unlock();
So, the counter value check and update must be performed atomically.
If you want to keep busy loop the easiest way would be just to call unlock before yield and lock after, so compare and decrement will be performed under the same lock:
void down() {
this->mutualExclusion.lock();
while (this->count == 0) {
this->mutualExclusion.unlock();
std::this_thread::yield();
this->mutualExclusion.lock();
}
this->count--;
this->mutualExclusion.unlock();
}
Also, you can use std::unique_lock guard which locks provided mutex in constructor and unlocks in destructor, so the mutex will not be accidentaly left in locked state:
void down() {
std::unique_lock<std::mutex> lock(this->mutualExclusion);
while (this->count == 0) {
lock.unlock();
std::this_thread::yield();
lock.lock();
}
this->count--;
}
To deal with "muxed destroyed while busy" error you need either to have a flag to stop background threads and wait for them to complete with join instead of detach:
#include <atomic>
...
std::atomic<bool> stopped{ false };
void philosopher(short i) {
while (!stopped) {
...
}
}
...
int main()
{
...
stopped = true;
for (int i = 0; i < N; i++) {
threadsVector[i]->join();
}
return 0;
}
or if you really don't want to care of releasing static resources you can call std::quick_exit instead of detach and return:
int main()
{
...
std::this_thread::sleep_for(std::chrono::milliseconds(15000));
std::quick_exit(0);
}

Why program works correctly while join only 1 thread but there are 5

This is my code and I accidentally made a mistake buy not making the for loop longer but the code works as intended.
What happens after the program is completed? Does it have any fail cases or does the computer auto kill all thread and if there would be any additional code it Threads again would there be problems( for example if i would initiate 2 thread then there would be 6 thread working and the new thread ids would be 5 and 7?)
#include <iomanip>
#include <thread>
#include <iostream>
#include <mutex>
#include <sstream>
#include <vector>
#include <conio.h>
using namespace std;
bool endProgram = false;
struct Monitorius {
public:
int IOCounter = 0;
int readCounterC = 0;
int readCounterD = 0;
condition_variable cv;
mutex mtx;
int c = 10;
int d = 100;
Monitorius() {
c = 10;
d = 100;
IOCounter = 0;
readCounterC = 0;
readCounterD = 0;
}
void changeC(int i) {
while (!endProgram) {
unique_lock<mutex> lck(mtx);
cv.wait(lck, [&] {return readCounterC > 1; });
if (!endProgram) {
c += i;
readCounterC = 0;
cv.notify_all();
}
}
}
void changeD(int i) {
while (!endProgram) {
unique_lock<mutex> lck(mtx);
cv.wait(lck, [&] {return readCounterD > 1; });
if (!endProgram) {
d -= i;
readCounterD = 0;
cv.notify_all();
}
}
}
void readCD(int i) {
int oldC = -1;
int oldD = -1;
while (!endProgram) {
unique_lock<mutex> lck(mtx);
cv.wait(lck, [&] {return oldC != c && oldD != d; });
if (!endProgram) {
stringstream str;
str << i << ": c:" << c << " d: " << d << endl;
cout << str.str();
readCounterC++;
readCounterD++;
IOCounter++;
if (IOCounter >= 15)
endProgram = true;
cv.notify_all();
oldC = c;
oldD = d;
}
}
}
};
int main()
{
Monitorius M;
vector<thread> myThreads;
myThreads.reserve(5);
myThreads.emplace_back([&] { M.changeC(1); });
myThreads.emplace_back([&] { M.changeD(2); });
myThreads.emplace_back([&] { M.readCD(3); });
myThreads.emplace_back([&] { M.readCD(4); });
myThreads.emplace_back([&] { M.readCD(5); });
for (size_t i = 0; i < 1; i++)
myThreads[i].join();
_getch();
}
When your main function exits, all the threads in the vector will be destructed.
If they are not joined at that time std::terminate should be called by the std::thread destructor.
By detaching threads the thread-objects can be destructed and the thread still continues to run. But then on the common modern operating systems when the process ends (which happens after main have returned or exit is called) the threads will be killed anyway. To let threads continue running even after the "main" thread ends, you have to call a system-dependent function to exit the "main" thread.
I do not know if it's possible to do this on Windows though, as the ExitThread function should not be used by C++ code as it exits the thread without destructing objects.
The solution to both problems is of course to properly join all threads.

Why is this c++ multithreading mutex code exhibiting occasional failures?

I am using this foo.cpp code below on a linux Debian system:
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <thread>
std::mutex mtx;
std::condition_variable cvar;
long next = 0;
void doit(long index){
std::unique_lock<std::mutex> lock(mtx);
cvar.wait(lock, [=]{return index == next;});
std::cout<< index << std::endl;
++next;
mtx.unlock();
cvar.notify_all();
return;
}
int main()
{
long n=50;
for (long i=0; i < n; ++i)
std::thread (doit,i).detach();
while(next != n)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return(0);
}
I compile it with:
g++ -std=c++14 -pthread -o foo foo.cpp
It is designed to fire off 50 threads, detached, which are controlled by a mutex and condition_variable in the function doit, so they execute the mutex block sequentially.
It works most of the time, writing the numbers 00 to 49 to the screen, and then terminating.
However, it has two occasional failure modes:
Failure mode 1: After going up to some arbitrary number < 50, it aborts with the error:
foo: ../nptl/pthread_mutex_lock.c:80: __pthread_mutex_lock: Assertion `mutex->__data.__owner == 0' failed.
Failure mode 2: After going up to some arbitrary number < 50, it hangs, and has to be killed with ctrl-C to get back to the terminal prompt.
I would appreciate any suggestions as to the causes of this behavior, and how I can fix it.
=========================================================================
Edit: Ok, so here is a working revised version. I fixed the two bugs, and also changed the lock name from "lock" to "lk" to reduce confusion. Thanks for the help.
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
std::mutex mtx;
std::condition_variable cvar;
long next = 0;
void doit(long index){
std::unique_lock<std::mutex> lk(mtx);
cvar.wait(lk, [=]{return index == next;});
std::cout<< index << std::endl;
++next;
lk.unlock();
cvar.notify_all();
return;
}
int main()
{
long n=50;
for (long i=0; i < n; ++i)
std::thread (doit,i).detach();
{
std::unique_lock<std::mutex> lk(mtx);
cvar.wait(lk, [=]{return n == next;});
}
return(0);
}
while(next != n) attempts to access variable next that can be modified by working threads without any sync creating a race condition. It should be covered by the same mutex:
{
std::unique_lock<std::mutex> lock(mtx);
cvar.wait(lock, [=]{return n == next;});
}
Detaching threads is not a good idea. You should store them somewhere and then join before returning from main.
Update: You are trying to call unlock on mutex itself instead of calling it on lock object. By constructing lock object you are delegating responsibility for unlocking mutex to lock object. It should be
lock.unlock();
cvar.notify_all();
I don't recommand detaching the threads, since you cannot join them after that.
If you really want to do it, then use condition variable to synchronize data for the while next.
void doit(long index){
std::unique_lock<std::mutex> lock(mtx);
cvar.wait(lock, [=]{return index == next;});
std::cout<< index << std::endl;
++next;
cvar.notify_all();
return;
}
int main()
{
long n=50;
for (long i=0; i < n; ++i)
std::thread (doit,i).detach();
//here you wait for the last thread to finish
{
std::unique_lock<std::mutex> lock(mtx);
cvar.wait(lock, [=]{return n == next;});
}
return(0);
}
If you can have your thread being joinable you can write simpler code.
std::mutex mtx;
std::condition_variable cvar;
long next = 0;
void doit(long index){
std::unique_lock<std::mutex> lock(mtx);
//this guarantees the order in which are being executed
cvar.wait(lock, [=]{return index == next;});
std::cout<< index << std::endl;
++next;
cvar.notify_all();//wakes all the thread, only the one with index=next will be executed
return;
}
int main()
{
long n=50;
std::vector<std::thread> workers;
for (long i=0; i < n; ++i){
workers.emplace_back(std::thread (doit,i));
}
//this guarantees your threads are all finished at the end of this block
for (auto& t : workers) {
t.join();
}
return(0);
}
Why not keep it simple?
int main() {
long n = 50;
std::vector<std::thread> threads;
for (long i = 0; i < n; ++i)
threads.emplace_back([=]() { std::cout << i << std::endl; });
for (const auto& t : threads) {
t.join();
}
return 0;
}
Try this snippet: You shouldn't use mtx.unlock() and let the condition_variable do the job. Also use std::ref to pass the function parameter to the thread.
std::mutex mtx;
std::condition_variable cvar;
bool ready = true;
void doit(long index) {
std::unique_lock<std::mutex> lock(mtx);
cvar.wait(lock, [=] {return ready == true; });
ready = false;
std::cout << index << std::endl;
ready = true;
cvar.notify_all();
return;
}
int main()
{
long n = 50;
for (long i = 0; i < n; ++i)
std::thread(doit, std::ref(i)).detach();
std::this_thread::sleep_for(std::chrono::seconds(3));
return(0);
}
std:: unique_lock is an RAII object. Declare it in a scope and cast your cares to the wind. Here's the problem: After doit calls mtx.unlock(), occasionally the next statement cvar.notify_all() will immediately wake up the thread with (the new) next == index. That thread will acquire the mutex. When doit returns, the lock destructor attempts to release the mutex, but it's held by another thread. Disaster ensues. Here's how to doit():
void doit(long index) {
{
std::unique_lock<std::mutex> lock(mtx);
cvar.wait(lock, [=] {return index == next; });
++next;
std::cout << index << std::endl;
}
cvar.notify_all();
return;
}