I wrote a code to implement spin lock and mutex lock.
There is an interesting but. A magic cout can keep my program alive. If I remove the cout, my program will be sleeping forever. (This only happens in Linux. Windows is doing fine)
Any one have a clue?
#include <pthread.h>
#include <iostream>
#include <queue>
#include <sys/time.h>
#include <stdexcept>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define Tcount 10
#define TheLock MutexLock
static inline int TAS(volatile int * ptr) {
unsigned long result;
asm volatile("lock;"
"xchgl %0, %1;"
: "=r"(result), "=m"(*ptr)
: "0"(1), "m"(*ptr)
: "memory");
return result;
}
class SpinLock {
private:
int lock;
pthread_t owner;
public:
SpinLock() {
lock = 0;
}
void getLock() {
while (TAS(&lock) == 1) {
}
owner = pthread_self();
}
void releaseLock() {
if (lock == 0) {
cout << "Spin no lock" << endl;
return;
} else if (owner == pthread_self()) {
owner = NULL;
lock = 0;
} else {
throw runtime_error("Spin can't release");
}
}
};
class MutexLock {
private:
int lock;
pthread_t owner;
queue<pthread_t> q;
SpinLock qLock;
public:
MutexLock() {
lock = 0;
}
void getLock(int id) {
pthread_t self = pthread_self();
cout<<"a"<<endl;// magic cout
if (TAS(&lock) == 0) {
owner = self;
return;
}
qLock.getLock();
q.push(self);
qLock.releaseLock();
while (owner != self) {
}
}
void releaseLock(int id) {
if (lock == 0) {
cout << "Mutex no lock" << endl;
return;
} else if (owner == pthread_self()) {
qLock.getLock();
if (q.empty()) {
owner = NULL;
lock = 0;
} else {
owner = q.front();
q.pop();
}
qLock.releaseLock();
} else {
throw runtime_error("Mutex can't release");
}
}
};
TheLock lock;
int g = 0;
void* run(void* pt) {
int id = (int) pt;
for (int i = 0; i < 10000; i++) {
lock.getLock(id);
//cout<<"Thread "<<id<<" get lock, g="<<g<<endl;
int next = g + 1;
g = next;
//cout<<"Thread "<<id<<" release lock, g="<<g<<endl;
lock.releaseLock(id);
}
return NULL;
}
int main() {
pthread_t th[Tcount];
long mtime, seconds, useconds;
struct timeval start, end;
gettimeofday(&start, NULL);
for (int i = 0; i < Tcount; i++) {
pthread_create(&th[i], NULL, run, (void*) (i+10));
}
for (int i = 0; i < Tcount; i++) {
pthread_join(th[i], 0);
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
mtime = ((seconds) * 1000000 + useconds);
cout << "g=" << g << endl;
cout << "time=" << mtime << endl;
return 0;
}
You cannot implement a mutex by using the volatile keyword as the operations may not be atomic. This means that the OS might switch to a different thread before the operation has completed.
For mutex you have to use the OS. It is the only thing that knows when threads are being switched.
Related
I designed a simple logging component, but I encountered some problems. The Logging object will create a thread to save logs from buffer in the background. The main thread writes logs into buffer. However, because I use pthread_detach, the main thread will exit even if the Logging thread is still working.
I use pthread_cond_t to solve that problem. I set the LastWriteTime, which represents the last time when main thread wrote to the log. If there has been no log for a period of time, the Logging thread will notify the main thread.
But the program blocks and never exits.
#include <string>
#include <cstring>
#include <string.h>
#include <iostream>
#include <pthread.h>
#include <sys/time.h>
using namespace std;
int64_t get_current_millis(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
void *run(void *args);
class Logging
{
public:
static Logging *LoggingPtr;
pthread_mutex_t ExitMutex;
pthread_cond_t ExitCond;
struct timeval LastWriteTime;
Logging() : ExitMutex(PTHREAD_MUTEX_INITIALIZER), ExitCond(PTHREAD_COND_INITIALIZER)
{
pthread_t pid;
pthread_create(&pid, NULL, run, NULL);
pthread_detach(pid);
}
bool CheckExpired(struct timeval lastWriteTime, size_t wait_time)
{
struct timeval now;
gettimeofday(&now, NULL);
long now_sec = now.tv_sec * 1000 + now.tv_usec / 1000;
long last_sec = lastWriteTime.tv_sec * 1000 + lastWriteTime.tv_usec / 1000;
// expired time: wait_time(ms)
return now_sec - last_sec > wait_time ? true : false;
}
void Save()
{
cout << "in the save" << endl;
while (true)
{
if (CheckExpired(LastWriteTime, 3000))
{
pthread_cond_signal(&ExitCond);
}
}
}
static Logging *Init()
{
while (!LoggingPtr)
{
LoggingPtr = new Logging();
}
return LoggingPtr;
}
void Append()
{
for (size_t i = 0; i < 100000; i++)
{
pthread_mutex_lock(&ExitMutex);
gettimeofday(&LastWriteTime, NULL);
pthread_mutex_unlock(&ExitMutex);
}
}
void Exit()
{
while (true)
{
if (CheckExpired(LastWriteTime, 3000))
{
pthread_cond_signal(&ExitCond);
}
}
pthread_mutex_lock(&ExitMutex);
// 3000 means that the wait_time is 3s
while (!CheckExpired(this->LastWriteTime, 3000))
{
pthread_cond_wait(&ExitCond, &ExitMutex);
}
pthread_mutex_unlock(&ExitMutex);
}
};
void *run(void *args)
{
Logging::Init()->Save();
return NULL;
}
Logging *Logging::LoggingPtr = nullptr;
int main()
{
uint64_t start_ts = get_current_millis();
Logging LOG;
LOG.Append();
LOG.Exit();
uint64_t end_ts = get_current_millis();
std::cout << "wait " << (end_ts - start_ts) / 1000 << "s" << std::endl;
return 0;
}
I am making a program using threads and a shared buffer. The two threads run indefinitely in the background, one thread will fill a shared buffer with data and the other thread will write the content of the shared buffer into a file.
The user can start or stop the data filling which is resulting in the thread entering into a waiting state until the user starts the thread again. Each loop the buffer is filled with 50 floats.
This is the code :
#include <iostream>
#include <vector>
#include <iterator>
#include <utility>
#include <fstream>
#include <condition_variable>
#include <mutex>
#include <thread>
using namespace std;
std::mutex m;
std::condition_variable cv;
std::vector<std::vector<float>> datas;
bool keep_running = true, start_running = false;
void writing_thread()
{
ofstream myfile;
bool opn = false;
while(1)
{
while(keep_running)
{
// Open the file only once
if(!opn)
{
myfile.open("IQ_Datas.txt");
opn = true;
}
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return !datas.empty();});
auto d = std::move(datas);
lk.unlock();
for(auto &entry : d)
{
for(auto &e : entry)
myfile << e << endl;
}
}
if(opn)
{
myfile.close();
opn = false;
}
}
}
void sending_thread()
{
std::vector<float> m_buffer;
int cpt=0;
//Fill the buffer with 50 floats
for(float i=0; i<50; i++)
m_buffer.push_back(i);
while(1)
{
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return keep_running && start_running;});
}
while(keep_running)
{
//Each loop d is containing 50 floats
std::vector<float> d = m_buffer;
cout << "in3" << endl; //Commenting this line makes the program crash
{
std::lock_guard<std::mutex> lk(m);
if (!keep_running)break;
datas.push_back(std::move(d));
}
cv.notify_one();
cpt++;
}
cout << "Total data: " << cpt*50 << endl;
cpt = 0;
}
}
void start()
{
{
std::unique_lock<std::mutex> lk(m);
start_running = true;
}
cv.notify_all();
}
void stop()
{
{
std::unique_lock<std::mutex> lk(m);
start_running = false;
}
cv.notify_all();
}
int main()
{
int go = 0;
thread t1(sending_thread);
thread t2(writing_thread);
t1.detach();
t2.detach();
while(1)
{
std::cin >> go;
if(go == 1)
{
start();
keep_running = true;
}
else if(go == 0)
{
stop();
keep_running = false;
}
}
return 0;
}
I have 2 issues with this code :
When commenting the line cout << "in3" << endl; the program will crash after ~20-40 seconds with the error message : terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc. If i let the cout, the program will run without problems.
When the program is working, after stoping sending_thread i display the total amount of data that has been copied with cout << "Total data: " << cpt*50 << endl;. For small amount of datas, all of it is written correctly into the file but when the amount is big, there is missing data. Missing/Correct data (Total number of lines in the file does not match total data)
Why with the cout the program is running correctly ? And what is causing the missing data ? Is it because sending_thread is filling the buffer too fast while writing_threadtakes too much time to write into the file?
EDIT: Some precisions, adding more cout into sending_threadseems to fix all the issues. First thread produced 21 million floats and second thread successfully wrote in the file 21 million floats. It seems like without the cout, producer threads works too fast for the consumer thread to keep retrieving data from the shared buffer while writing it into a file.
To avoid:
Moved-from object 'datas' of type 'std::vector' is moved:
auto d = std::move(datas);
^~~~~~~~~~~~~~~~
Replace this:
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return !datas.empty();});
auto d = std::move(datas);
lk.unlock();
With this:
// Wait until main() sends data
std::vector<std::vector<float>> d;
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] { return !datas.empty(); });
datas.swap(d);
}
Also replace your bool variables that are accessed from multiple threads with std::atomic_bool or std::atomic_flag.
The bad_alloc comes from sending_thread being much faster than writing_thread so it will run out of memory. When you slow down sending_thread enough (with printing), the problem is less visible, but you should have some synchronization to do it properly. You could make a wrapper class around it and provide insert and extraction methods to make sure all access is synchronized properly and also give it a max number of elements. An example:
template<typename T>
class atomic2dvector {
public:
atomic2dvector(size_t max_elements) : m_max_elements(max_elements) {}
atomic2dvector(const atomic2dvector&) = delete;
atomic2dvector(atomic2dvector&&) = delete;
atomic2dvector& operator=(const atomic2dvector&) = delete;
atomic2dvector& operator=(atomic2dvector&&) = delete;
~atomic2dvector() { shutdown(); }
bool insert_one(std::vector<T>&& other) {
std::unique_lock<std::mutex> lock(m_mtx);
while(m_current_elements + m_data.size() > m_max_elements && m_shutdown == false)
m_cv.wait(lock);
if(m_shutdown) return false;
m_current_elements += other.size();
m_data.emplace_back(std::forward<std::vector<T>>(other));
m_cv.notify_one();
return true;
}
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
if(m_shutdown == false) {
m_current_elements = 0;
return_value.swap(m_data);
} else {
// return an empty vector if we should shutdown
}
m_cv.notify_one();
return return_value;
}
bool is_active() const { return m_shutdown == false; }
void shutdown() {
m_shutdown = true;
m_cv.notify_all();
}
private:
size_t m_max_elements;
size_t m_current_elements = 0;
std::atomic<bool> m_shutdown = false;
std::condition_variable m_cv{};
std::mutex m_mtx{};
std::vector<std::vector<T>> m_data{};
};
If you'd like to keep extracting data even after shutdown, you can change extract_all() to this:
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
m_current_elements = 0;
return_value.swap(m_data);
m_cv.notify_one();
return return_value;
}
A full example could look like this:
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <iterator>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
using namespace std;
template<typename T>
class atomic2dvector {
public:
atomic2dvector(size_t max_elements) : m_max_elements(max_elements) {}
atomic2dvector(const atomic2dvector&) = delete;
atomic2dvector(atomic2dvector&&) = delete;
atomic2dvector& operator=(const atomic2dvector&) = delete;
atomic2dvector& operator=(atomic2dvector&&) = delete;
~atomic2dvector() { shutdown(); }
bool insert_one(std::vector<T>&& other) {
std::unique_lock<std::mutex> lock(m_mtx);
while(m_current_elements + m_data.size() > m_max_elements &&
m_shutdown == false)
m_cv.wait(lock);
if(m_shutdown) return false;
m_current_elements += other.size();
m_data.emplace_back(std::forward<std::vector<T>>(other));
m_cv.notify_one();
return true;
}
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
m_current_elements = 0;
return_value.swap(m_data);
m_cv.notify_one();
return return_value;
}
bool is_active() const { return m_shutdown == false; }
void shutdown() {
m_shutdown = true;
m_cv.notify_all();
}
private:
size_t m_max_elements;
size_t m_current_elements = 0;
std::atomic<bool> m_shutdown = false;
std::condition_variable m_cv{};
std::mutex m_mtx{};
std::vector<std::vector<T>> m_data{};
};
std::mutex m;
std::condition_variable cv;
atomic2dvector<float> datas(256 * 1024 * 1024 / sizeof(float)); // 0.25 GiB limit
std::atomic_bool start_running = false;
void writing_thread() {
std::ofstream myfile("IQ_Datas.txt");
if(myfile) {
std::cout << "writing_thread waiting\n";
std::vector<std::vector<float>> d;
while((d = datas.extract_all()).empty() == false) {
std::cout << "got " << d.size() << "\n";
for(auto& entry : d) {
for(auto& e : entry) myfile << e << "\n";
}
std::cout << "wrote " << d.size() << "\n\n";
}
}
std::cout << "writing_thread shutting down\n";
}
void sending_thread() {
std::vector<float> m_buffer;
std::uintmax_t cpt = 0;
// Fill the buffer with 50 floats
for(float i = 0; i < 50; i++) m_buffer.push_back(i);
while(true) {
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {
return start_running == true || datas.is_active() == false;
});
}
if(datas.is_active() == false) break;
std::cout << "sending...\n";
while(start_running == true) {
// Each loop d is containing 50 floats
std::vector<float> d = m_buffer;
if(datas.insert_one(std::move(d)) == false) break;
cpt++;
}
cout << "Total data: " << cpt * 50 << endl;
cpt = 0;
}
std::cout << "sending_thread shutting down\n";
}
void start() {
std::unique_lock<std::mutex> lk(m);
start_running = true;
cv.notify_all();
}
void stop() {
std::unique_lock<std::mutex> lk(m);
start_running = false;
cv.notify_all();
}
void quit() {
datas.shutdown();
cv.notify_all();
}
int main() {
int go = 0;
thread t1(sending_thread);
thread t2(writing_thread);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Enter 1 to make the sending thread send and 0 to make it stop "
"sending. Enter a non-integer to shutdown.\n";
while(std::cin >> go) {
if(go == 1) {
start();
} else if(go == 0) {
stop();
}
}
std::cout << "--- shutting down ---\n";
quit();
std::cout << "joining threads\n";
t1.join();
std::cout << "t1 joined\n";
t2.join();
std::cout << "t2 joined\n";
}
My application start the logThread thread and then cyclically calls lookForSms wich makes a curl call and wait for logThread to read a certain string, but this string may never come so i used a condition_variable wait_for so that eventually (after 5 minutes) it can continue the test.
The problem is at times it waits a lot longer, sometimes 20 min sometimes forever.
How can I make it reliably work?
I'm using Visual Studio Community 2017, here the code:
void lookForSms(CURL *curl) {
sms_found = false;
int i;
CURLcode res;
cout << "\n" << timestamp() << "\t";
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
unique_lock<mutex> lk(F);
analysis << timestampMil() << "\t";
while (cv.wait_for(lk, TIMEOUT_SMS * 1000ms) != cv_status::timeout && !sms_found);
if (sms_found) {
analysis << timestampMil() << "\tSMS RECEIVED\n";
cout << "\tOK";
}
else {
analysis << timestampMil() << "\tSMS LOST\n";
cout << "\tKO";
}
}
}
void logThread() {
string line;
int n, i, count;
unsigned char buf[BUFFER];
while (!close) {
N.lock();
M.lock();
N.unlock();
n = RS232_PollComport(comport, buf, BUFFER);
if (n > 0 && n < BUFFER) {
buf[n] = '\0';
for (i = 0; i < n; i++) {
if (buf[i] == '\n') {
all << timestampMil() << "\t" << line << "\n";
handleLine(line);
line = string();
}
else if (buf[i] != '\r' && buf[i] >= 32) {
line += buf[i];
}
}
}
M.unlock();
}
}
void handleLine(string line) {
if (searchString(line, RCV_SMS)) {
unique_lock<mutex> lk(F);
sms_found = true;
lk.unlock();
cv.notify_all();
}
}
[EDIT] This should have everything it needs to recreate the problem.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#define TIMEOUT_SMS 300
#define DELAY_SMS 10
mutex L, M, N, F;
condition_variable cv;
void logThread() {
while (!close) {
N.lock();
M.lock();
N.unlock();
handleLine("");
Sleep(500);
M.unlock();
}
}
void lookForSms() {
unique_lock<mutex> lk(F);
sms_found = false;
unlocked = false;
//while (cv.wait_for(lk, TIMEOUT_SMS * 1000ms) != cv_status::timeout && !unlocked);
cv.wait_for(lk, 10 * TIMEOUT_SMS * 1000ms, []() {return unlocked; });
if (sms_found) {
cout << "\tOK";
}
else {
cout << "\tKO";
}
}
}
void handleLine(string line) {
if (false) {// the problem is when cv remains locked
unique_lock<mutex> lk(F);
unlocked = true;
sms_found = true;
lk.unlock();
cv.notify_all();
}
}
void stopThreads() {
N.lock();
M.lock();
//stop log thread
close = true;
N.unlock();
M.unlock();
}
int main(int argc, const char * argv[]) {
thread logger(logThread);
for (i = 0; i < repeat; i++) {
lookForSms();
Sleep(DELAY_SMS * 1000);
}
//aquire mutex and set close to true, then join
stopThreads();
//wait for the thread to end
logger.join();
return 0;
}
I am trying to create an application which create one main thread and 10 slave threads. I want to run the slave threads once after the main thread is run. So for each main thread execution, each slave thread is going to execute once. I tried to handle this with two different conditional variables. So, one is used for slave threads so they can wait until the main thread notify them and another conditional variable for the main thread which is signaled after each child finish its task, so the main thread can check if all the slave threads are done or not. The code is as follows:
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
std::vector<boost::thread*> threads;
std::vector<boost::mutex*> data_ready_mutex;
std::vector<boost::condition_variable*> cond;
std::vector<bool> data_ready;
std::vector<int> num_run;
boost::mutex check_finish_mutex;
std::vector<bool> finished;
boost::atomic<int> data;
boost::atomic<int> next_thread_id;
boost::mutex finished_task_mutex;
boost::condition_variable finished_task_cond;
bool finished_task = false;
void signal_finished(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(finished_task_mutex);
finished[id] = true;
finished_task = true;
}
finished_task_cond.notify_all();
}
void signal_slave(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(*data_ready_mutex[id]);
data_ready[id] = true;
}
cond[id]->notify_all();
}
void slave_therad()
{
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
boost::unique_lock<boost::mutex> lock(*data_ready_mutex[id]);
while (!data_ready[id])
{
cond[id]->wait(lock);
}
finished[id] = false;
data_ready[id] = false;
data++;
num_run[id]++;
signal_finished(id);
}
}
void main()
{
size_t nThreads = 10;
data_ready_mutex.resize(nThreads);
cond.resize(nThreads);
data_ready.resize(nThreads);
finished.resize(nThreads);
num_run.resize(nThreads, 0);
for (size_t i = 0; i < nThreads; i++)
{
data_ready_mutex[i] = new boost::mutex();
cond[i] = new boost::condition_variable();
data_ready[i] = false;
finished[i] = false;
}
for (size_t i = 0; i < nThreads; i++)
{
threads.push_back(new boost::thread(slave_therad));
}
while (true)
{
clock_t start_time = clock();
for (size_t i = 0; i < threads.size(); i++)
signal_slave(static_cast<int>(i));
while (true)
{
boost::unique_lock<boost::mutex> lock(finished_task_mutex);
while (!finished_task)
{
finished_task_cond.wait(lock);
}
finished_task = false;
size_t i = 0;
for (; i < finished.size(); i++)
{
if (!finished[i]) break;
}
if (i == finished.size()) break;
}
clock_t end_time = clock();
std::cout << "Elapsed Time = " << static_cast<float>(end_time - start_time) / CLOCKS_PER_SEC << std::endl;
for (size_t i = 0; i < threads.size(); i++)
finished[i] = false;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
The problem is that somewhere the code stops and it stuck in deadlock.
Also, I tried to change the way to implement. So, I used an atomic<int> which counts the number of threads which has finished their task and in the main thread I check if the number of threads is equal to number of threads which has updated themselves but this method also stuck somewhere and goes into deadlock.
The code can be found here:
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
std::vector<boost::thread*> threads; //!< Slave Threads array
std::vector<boost::mutex*> data_ready_mutex; //!< Mutex to guard the data_ready
std::vector<bool> data_ready; //!< Shows if the data is ready for the slave thread or not.
std::vector<boost::condition_variable*> cond; //!< conditional variable to wait on data being ready for the slave thread.
std::vector<int> num_run; //!< Stores the number of times each slave thread is run.
boost::atomic<int> data; //!< Stores the data processed by each slave thread
boost::atomic<int> next_thread_id; //!< id for the next thread (used for giving an id from 0,..., nThreads-1
boost::atomic<int> num_threads_done; //!< Stores the number of slave threads which has finished their task
//! Signals a slave thread to start its task
void signal_slave(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(*data_ready_mutex[id]);
data_ready[id] = true;
}
cond[id]->notify_all();
}
//! Slave thread function
void slave_therad()
{
// assign an id to the current slave_thread
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
// wait for a signal from the main thread
boost::unique_lock<boost::mutex> lock(*data_ready_mutex[id]);
while (!data_ready[id])
{
cond[id]->wait(lock);
}
// make the data not ready, so the loop is not going to run without the main thread signal after the thread is done.
data_ready[id] = false;
// TASK for SLAVE THREAD
data++;
// Increase the number of times the thread is run
num_run[id]++;
// Increase the number of threads which has finished their tasks.
num_threads_done++;
}
}
void main()
{
size_t nThreads = 10;
// creating the data ready mutexes, conditional variables, data_ready variable (bools), num_runs array.
data_ready_mutex.resize(nThreads);
cond.resize(nThreads);
data_ready.resize(nThreads);
num_run.resize(nThreads, 0);
for (size_t i = 0; i < nThreads; i++)
{
data_ready_mutex[i] = new boost::mutex();
cond[i] = new boost::condition_variable();
data_ready[i] = false;
}
// Creating the slave threads
for (size_t i = 0; i < nThreads; i++)
{
threads.push_back(new boost::thread(slave_therad));
}
// Main Thread Body
while (true)
{
clock_t start_time = clock();
// Reset the number of threads which are done.
num_threads_done = 0;
// Signals the slave threads to start doing their task.
for (size_t i = 0; i < threads.size(); i++)
signal_slave(static_cast<int>(i));
// Wait until all the slave threads are done.
while (true)
if (num_threads_done == threads.size()) break;
clock_t end_time = clock();
std::cout << "Elapsed Time = " << static_cast<float>(end_time - start_time) / CLOCKS_PER_SEC << std::endl;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
Even, I tried to fix the issue with barriers but it did not fix my problem. the code is as follows:
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
boost::barrier* barrier; //!< barrier to make sure all the slave threads are done their tasks.
std::vector<boost::thread*> threads;
std::vector<boost::mutex*> data_ready_mutex; //!< Mutex to guard the data_ready
std::vector<bool> data_ready; //!< Shows if the data is ready for the slave thread or not.
std::vector<boost::condition_variable*> cond; //!< conditional variable to wait on data being ready for the slave thread.
std::vector<int> num_run; //!< Stores the number of times each slave thread is run.
boost::atomic<int> data; //!< Stores the data processed by each slave thread
boost::atomic<int> next_thread_id; //!< id for the next thread (used for giving an id from 0,..., nThreads-1
boost::atomic<int> num_threads_done; //!< Stores the number of slave threads which has finished their task
std::vector<bool> finished; //!< Array which stores if all the slave threads are done or not.
boost::mutex finished_task_mutex; //!< mutex to guard the finished_task variable
boost::condition_variable finished_task_cond; //!< Conditional variable to wait for all the threads to finish they tasks.
boost::atomic<bool> finished_task(false); //!< Variable which stores if the task of slave_threads are finished or not.
void signal_finished(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(finished_task_mutex);
finished[id] = true;
finished_task = true;
}
finished_task_cond.notify_all();
}
void signal_slave(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(*data_ready_mutex[id]);
data_ready[id] = true;
}
cond[id]->notify_all();
}
void slave_therad()
{
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
boost::unique_lock<boost::mutex> lock(*data_ready_mutex[id]);
while (!data_ready[id])
{
cond[id]->wait(lock);
}
finished[id] = false;
data_ready[id] = false;
data++;
num_run[id]++;
barrier->wait();
signal_finished(id);
}
}
void main()
{
size_t nThreads = 10;
data_ready_mutex.resize(nThreads);
cond.resize(nThreads);
data_ready.resize(nThreads);
finished.resize(nThreads);
num_run.resize(nThreads, 0);
for (size_t i = 0; i < nThreads; i++)
{
data_ready_mutex[i] = new boost::mutex();
cond[i] = new boost::condition_variable();
data_ready[i] = false;
finished[i] = false;
}
barrier = new boost::barrier(nThreads);
for (size_t i = 0; i < nThreads; i++)
{
threads.push_back(new boost::thread(slave_therad));
}
while (true)
{
clock_t start_time = clock();
for (size_t i = 0; i < threads.size(); i++)
signal_slave(static_cast<int>(i));
while (true)
{
boost::unique_lock<boost::mutex> lock(finished_task_mutex);
while (!finished_task)
{
finished_task_cond.wait(lock);
}
finished_task = false;
break;
}
clock_t end_time = clock();
std::cout << "Elapsed Time = " << static_cast<float>(end_time - start_time) / CLOCKS_PER_SEC << std::endl;
for (size_t i = 0; i < threads.size(); i++)
finished[i] = false;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
[UPDATED]
So, I simply used the mutex, conditional variables and data_ready in a struct as follows and now the code is working. I think there was a bug with using pointer to mutex and so on. the code is as follows:
//#define SYNC_WITH_BARRIER
#define SYNC_WITH_ATOMICS
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
std::vector<boost::thread*> threads;
boost::atomic<int> next_thread_id(0);
boost::mutex finished_task_mutex;
boost::condition_variable finished_task_cond;
bool finished_task = false;
boost::atomic<int> num_finished_tasks(0);
struct Work
{
Work(boost::barrier& _barrier) : b(&_barrier)
{
}
boost::barrier* b;
boost::mutex data_ready_mutex;
boost::condition_variable data_ready_cond;
bool data_ready;
int num_run;
boost::atomic<int> data;
bool finished;
void signal_slave()
{
{
boost::lock_guard<boost::mutex> lock(data_ready_mutex);
data_ready = true;
data_ready_cond.notify_all();
}
}
void slave_therad()
{
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
boost::unique_lock<boost::mutex> lock(data_ready_mutex);
while (!data_ready)
{
data_ready_cond.wait(lock);
}
finished = false;
data_ready = false;
data++;
num_run++;
#ifdef SYNC_WITH_BARRIER
b->count_down_and_wait();
#else
#ifdef SYNC_WITH_ATOMICS
num_finished_tasks++;
#endif
#endif
}
}
};
#include <boost/chrono.hpp>
#include <boost/chrono/chrono_io.hpp>
using hrc = boost::chrono::high_resolution_clock;
void main()
{
size_t nThreads = 10;
boost::thread_group tg;
boost::ptr_vector<Work> work_items;
work_items.reserve(nThreads);
boost::barrier finish(nThreads + 1); // one for the main thread
for (size_t i = 0; i < nThreads; i++)
{
work_items.push_back(new Work(finish));
tg.create_thread(boost::bind(&Work::slave_therad, boost::ref(work_items.back())));
}
while (true)
{
auto start_time = hrc::now();
num_finished_tasks = 0;
for (size_t i = 0; i < work_items.size(); i++)
work_items[i].signal_slave();
#ifdef SYNC_WITH_BARRIER
finish.count_down_and_wait();
#else
#ifdef SYNC_WITH_ATOMICS
while (true) if (num_finished_tasks == work_items.size()) break;
#endif
#endif
clock_t end_time = clock();
std::cout << "Elapsed Time = " << hrc::now() - start_time << std::endl;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
#sehe even with barrier, it stuck in deadlock. – mmostajab 5 mins ago
Since youdon't show anything about what you're doing there, let me give you a startup boost by incorporating a large chunk of all the suggestions you received:
Live On Coliru
#include <boost/atomic.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
namespace /*static*/ {
boost::atomic<int> data;
boost::atomic<int> num_threads_done;
struct Work {
void signal_slave()
{
boost::lock_guard<boost::mutex> lock(data_ready_mutex);
data_ready = true;
cond.notify_all();
}
void slave_thread()
{
static boost::atomic_int _id_gen(0);
id = _id_gen++;
std::cout << "(" << id << ") slave_thread created\n";
while (true) {
boost::unique_lock<boost::mutex> lock(data_ready_mutex);
cond.wait(lock, [&]{ return data_ready; });
data_ready = false;
data++;
num_run++;
num_threads_done++;
}
}
private:
int id = 0;
bool data_ready = false;
int num_run = 0;
boost::mutex data_ready_mutex;
boost::condition_variable cond;
};
}
#include <boost/chrono.hpp>
#include <boost/chrono/chrono_io.hpp>
using hrc = boost::chrono::high_resolution_clock;
int main()
{
boost::thread_group tg;
size_t nThreads = 10;
std::vector<Work> works(nThreads);
for (size_t i = 0; i < nThreads; i++) {
tg.create_thread(boost::bind(&Work::slave_thread, boost::ref(works[i])));
}
while (true) {
auto start_time = hrc::now();
for (auto& w : works)
w.signal_slave();
std::cout << "Elapsed Time = " << (hrc::now()-start_time) << std::endl;
}
tg.join_all();
}
Bear in mind, I don't know what you're trying to achieve here. Adding a barrier I had this in mind: how to use boost barrier
I tried to change the #sehe answer, so it solve exactly the problem which I am looking for and I achieved this code:
#include <boost/atomic.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
namespace /*static*/ {
boost::atomic<int> data;
boost::barrier* slave_thread_finished_barrier;
boost::mutex slave_thread_finished_mutex;
boost::condition_variable slave_thread_finished_cond;
bool slave_thread_finished = false;
struct Work {
void signal_slave()
{
boost::lock_guard<boost::mutex> lock(data_ready_mutex);
data_ready = true;
cond.notify_all();
}
void slave_thread()
{
static boost::atomic_int _id_gen(0);
id = _id_gen++;
std::cout << "(" << id << ") slave_thread created\n";
while (true) {
boost::unique_lock<boost::mutex> lock(data_ready_mutex);
cond.wait(lock, [&]{ return data_ready; });
data_ready = false;
data++;
num_run++;
slave_thread_finished_barrier->wait();
// signaling the main thread that the slave threads are done.
if (id == 0)
{
boost::lock_guard<boost::mutex> lock(slave_thread_finished_mutex);
slave_thread_finished = true;
slave_thread_finished_cond.notify_one();
}
}
}
private:
int id = 0;
bool data_ready = false;
int num_run = 0;
boost::mutex data_ready_mutex;
boost::condition_variable cond;
};
}
#include <boost/chrono.hpp>
#include <boost/chrono/chrono_io.hpp>
using hrc = boost::chrono::high_resolution_clock;
int main()
{
boost::thread_group tg;
size_t nThreads = 10;
slave_thread_finished_barrier = new boost::barrier(nThreads);
std::vector<Work> works(nThreads);
for (size_t i = 0; i < nThreads; i++) {
tg.create_thread(boost::bind(&Work::slave_thread, boost::ref(works[i])));
}
while (true) {
auto start_time = hrc::now();
for (auto& w : works)
w.signal_slave();
// Wait for slave threads to finish.
boost::unique_lock<boost::mutex> lock(slave_thread_finished_mutex);
slave_thread_finished_cond.wait(lock, [&]{ return slave_thread_finished; });
slave_thread_finished = false;
std::cout << "Elapsed Time = " << (hrc::now() - start_time) << std::endl;
}
tg.join_all();
}
i have 3 thread and 2 shared resources, which need some locking...i tried to illustrate the resources with 2 buffers...
- thread 1 can only access resource 1
- thread 2 can access resource 1 and 2
- thread 3 can access resource 1 and 2
can someone tell me why the following locking fails? since thread2 and thread3 will access resource 1 and 2...i thought i could use try_lock? ...it seems the issue pops up, when thread2 and thread3 is only able to lock 1 mutex at a time...any idea?
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;
class SynchronizationTest {
private:
mutex lock_r1;
mutex lock_r2;
vector<pair<string, int>> buffer_r1;
vector<pair<string, int>> buffer_r2;
unsigned int buffer_r1_max_size;
unsigned int buffer_r2_max_size;
bool buffer_r1_inc_element(const string &thread_id) {
if (buffer_r1.size() == buffer_r1_max_size) {
return true;
}
if (buffer_r1.size() == 0) {
buffer_r1.push_back(make_pair(thread_id, 0));
}
else {
int last_val = buffer_r1.back().second;
buffer_r1.push_back(make_pair(thread_id, ++last_val));
}
return false;
}
bool buffer_r2_inc_element(const string &thread_id) {
if (buffer_r2.size() == buffer_r2_max_size) {
return true;
}
if (buffer_r2.size() == 0) {
buffer_r2.push_back(make_pair(thread_id, 0));
}
else {
int last_val = buffer_r2.back().second;
buffer_r2.push_back(make_pair(thread_id, ++last_val));
}
return false;
}
public:
SynchronizationTest(int buff_r1_size, int buff_r2_size) : buffer_r1_max_size(buff_r1_size),
buffer_r2_max_size(buff_r2_size) {}
void thread1() {
bool buffer_r1_full = false;
while (!buffer_r1_full) {
{
unique_lock<mutex> l(lock_r1, std::defer_lock);
if (l.try_lock()) {
buffer_r1_full = buffer_r1_inc_element("thread1");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void thread2() {
bool buffer_r1_full = false;
bool buffer_r2_full = false;
while (!buffer_r1_full || !buffer_r2_full) {
{
unique_lock<mutex> lock1(lock_r1, defer_lock);
unique_lock<mutex> lock2(lock_r2, defer_lock);
int result = try_lock(lock1, lock2);
if(result == -1) {
buffer_r1_full = buffer_r1_inc_element("thread2");
buffer_r2_full = buffer_r2_inc_element("thread2");
}
else if(result != 0) {
buffer_r1_full = buffer_r1_inc_element("thread2");
}
else if(result != 1) {
buffer_r2_full = buffer_r2_inc_element("thread2");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void thread3() {
bool buffer_r1_full = false;
bool buffer_r2_full = false;
while (!buffer_r1_full || !buffer_r2_full) {
{
unique_lock<mutex> lock1(lock_r1, defer_lock);
unique_lock<mutex> lock2(lock_r2, defer_lock);
int result = try_lock(lock1, lock2);
if(result == -1) {
buffer_r1_full = buffer_r1_inc_element("thread3");
buffer_r2_full = buffer_r2_inc_element("thread3");
}
else if(result != 0) {
buffer_r1_full = buffer_r1_inc_element("thread3");
}
else if(result != 1) {
buffer_r2_full = buffer_r2_inc_element("thread3");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void print_buffer() {
for_each(buffer_r1.begin(), buffer_r1.end(), [](pair<string, int> p) { cout << p.first.c_str() << " " << p.second << endl; });
cout << '\n';
for_each(buffer_r2.begin(), buffer_r2.end(), [](pair<string, int> p) { cout << p.first.c_str() << " " << p.second << endl; });
}
};
int main() {
// your code goes here
SynchronizationTest st(20, 20);
thread t1(&SynchronizationTest::thread1, &st);
thread t2(&SynchronizationTest::thread2, &st);
thread t3(&SynchronizationTest::thread3, &st);
t1.join();
t2.join();
t3.join();
st.print_buffer();
return 0;
}
std::try_lock does not work that way. If it returns -1, all locks are held. If it returns a non-negative integer, no locks are held. The returned value tells which lock failed, but any locks that were locked successfully are released before try_lock returns.
problem solved:
unique_lock<mutex> lock1(lock_r1, defer_lock);
unique_lock<mutex> lock2(lock_r2, defer_lock);
bool result1 = lock1.try_lock();
bool result2 = lock2.try_lock();
if(result1 && result2) {
buffer_r1_full = buffer_r1_inc_element("thread2");
buffer_r2_full = buffer_r2_inc_element("thread2");
}
else if(result1) {
buffer_r1_full = buffer_r1_inc_element("thread2");
}
else if(result2) {
buffer_r2_full = buffer_r2_inc_element("thread2");
}