Thread notification isn't working for my consumer - multithreading

I have an attempt at a producer/consumer
Producer
#pragma once
#ifndef PRODUCER_H
#define PRODUCER_H
#include <thread>
#include "Mailbox.h"
class Producer
{
private:
std::thread producer;
Mailbox& mailbox;
public:
Producer(Mailbox& newmailbox);
~Producer();
void start();
void run();
};
Producer::Producer(Mailbox& newMailbox) : mailbox(newMailbox) {}
Producer::~Producer() {}
void Producer::start()
{
producer = std::thread(&Producer::run, this);
}
void Producer::run()
{
mailbox.inc();
}
#endif
Consumer
#pragma once
#ifndef CONSUMER_H
#define CONSUMER_H
#include "Mailbox.h"
#include <thread>
#include <iostream>
class Consumer
{
private:
Mailbox& mailbox;
std::thread consumer;
public:
Consumer(Mailbox& newMailbox);
~Consumer();
void start();
void run();
};
Consumer::Consumer(Mailbox& newMailbox) : mailbox(newMailbox) {}
Consumer::~Consumer() {}
void Consumer::start()
{
consumer = std::thread(&Consumer::run, this);
}
void Consumer::run()
{
mailbox.read();
}
#endif
Mailbox
#pragma once
#ifndef MAILBOX_H
#define MAILBOX_H
#include <mutex>
#include <iostream>
class Mailbox
{
private:
int& mailbox;
int init_val;
std::mutex mmutex;
std::condition_variable condition;
public:
Mailbox();
~Mailbox();
void inc();
void read();
};
Mailbox::Mailbox() : mailbox(init_val), init_val(0) {}
Mailbox::~Mailbox()
{
}
void Mailbox::inc()
{
int count = 0;
while (count < 10)
{
std::unique_lock<std::mutex> lock(mmutex);
std::cout << "Producer increment\n";
mailbox += 1;
lock.unlock();
count += 1;
}
}
void Mailbox::read()
{
int count = 0;
while (count < 10)
{
std::unique_lock<std::mutex> lock(mmutex);
condition.wait(lock, [this](){return get_cflag(); });
condition.notify_one();
count += 1;
}
}
#endif
Main
int main()
{
Mailbox* mailbox = new Mailbox();
Consumer* consumer = new Consumer(*mailbox);
Producer* producer = new Producer(*mailbox);
consumer->start();
producer->start();
return 0;
}
Mutex locking works albeit asynchronously because I have no control over when a std::thread will start so I decided to implement a semi-synchronous methodology using std::unique_lock in addition to std::mutex.
Problem is, the Consumer waits and the Producer flies on ahead with no notification at least that is what the debugger is telling me and the last Producer iteration result sin a n abort() so something is going wrong here.

Based upon David Schwartz's comment, insight from Mike Strobel, and additional research, I changed the producer and consumer functions
Producer
void Mailbox::inc()
{
int count = 0;
while (count < 10)
{
std::unique_lock<std::mutex> lock(mmutex);
std::cout << "Producer increment\n";
mailbox += 1;
lock.unlock();
set_cflag(true); // signal to the consumer data is ready
condition.notify_one();
{
std::unique_lock<std::mutex> lock(mmutex);
condition.wait(lock, [this]() {return get_pflag(); });
}
set_pflag(false);
count += 1;
}
}
Consumer
void Mailbox::read()
{
int count = 0;
while (count < 10)
{
std::unique_lock<std::mutex> lock(mmutex);
condition.wait(lock, [this](){return get_cflag(); });
std::cout << "Consumer: " << mailbox << "\n";
lock.unlock();
set_pflag(true);
condition.notify_one();
count += 1;
set_cflag(false);
}
}
Mailbox
class Mailbox
{
private:
int& mailbox;
int cflag, pflag;
int init_val;
std::mutex mmutex;
std::condition_variable condition;
public:
Mailbox();
~Mailbox();
int get_cflag() { return cflag; }
void set_cflag(int newFlag) { cflag = newFlag; }
int get_pflag() { return pflag; }
void set_pflag(int newFlag) { pflag = newFlag; }
void inc();
void read();
};
Mailbox::Mailbox() : mailbox(init_val), init_val(0), cflag(0), pflag(0) {}
Mailbox::~Mailbox()
{
}
The output upon execution is as I desired
int main()
{
Mailbox* mailbox = new Mailbox();
Consumer* consumer = new Consumer(*mailbox);
Producer* producer = new Producer(*mailbox);
consumer->start();
producer->start();
fgetc(stdin);
return 0;
}
Producer increment
Consumer: 1
Producer increment
Consumer: 2
Producer increment
Consumer: 3
Producer increment
Consumer: 4
Producer increment
Consumer: 5
Producer increment
Consumer: 6
Producer increment
Consumer: 7
Producer increment
Consumer: 8
Producer increment
Consumer: 9
Producer increment
Consumer: 10

I’m not a C++ guy, but if these condition variables work the way I think they do, you’ll only get notified if a signal arrives while you’re waiting. If the signal arrived before you started waiting, you’ll block indefinitely.
After you acquire the lock in 'Mailbox::read`, you should check to see if an item is available, and only wait on the condition variable if there isn’t one. If there is, go ahead and take it:
int Mailbox::read()
{
std::unique_lock<std::mutex> lock(m);
while (mailbox <= 0)
condition.wait(lock);
return mailbox--;
}

Related

Regarding thread communication to post task back from called child thread to main thread

I have a requirement to post a task from child thread to main thread back. I am creating child thread from the main thread and posting tasks over there. But after receiving few callbacks from common API, I need to execute a few particular tasks on main thread only like proxy creation, etc. so in such a scenario, I have to communicate with the main thread and need to post that particular task on the main thread. I have designed LoopingThread.cpp as mentioned below and communicating with the main for posting tasks on that:
LoopingThread.cpp:
#include <iostream>
#include "loopingThread.hpp"
using namespace std;
LoopingThread::LoopingThread() : thread(nullptr), scheduledCallbacks() {
}
LoopingThread::~LoopingThread() {
if (this->thread) {
delete this->thread;
}
}
void LoopingThread::runCallbacks() {
this->callbacksMutex.lock();
if (this->scheduledCallbacks.size() > 0) {
std::thread::id threadID = std::this_thread::get_id();
std::cout<<"inside runCallback()threadId:"<<threadID<<std::endl;
// This is to allow for new callbacks to be scheduled from within a callback
std::vector<std::function<void()>> currentCallbacks = std::move(this->scheduledCallbacks);
this->scheduledCallbacks.clear();
this->callbacksMutex.unlock();
for (auto callback : currentCallbacks) {
//callback();
//this->callback();
int id = 1;
this->shared_func(id);
}
} else {
this->callbacksMutex.unlock();
}
}
void LoopingThread::shared_func(int id)
{
std::thread::id run_threadID = std::this_thread::get_id();
std::cout<<"shared_func: "<<run_threadID<<std::endl;
this->callbacksMutex.lock();
if (id == 0)
std::cout<<"calling from main,id: "<<id<<std::endl;
else if (id == 1)
std::cout<<"calling from child,id: "<<id<<std::endl;
this->callbacksMutex.unlock();
}
void LoopingThread::run() {
std::thread::id run_threadID = std::this_thread::get_id();
std::cout<<"Child_run_threadID: "<<run_threadID<<std::endl;
for (;;) {
this->runCallbacks();
// Run the tick
if (!this->tick()) {
std::cout<<"Run the tick"<<std::endl;
break;
}
}
// Run pending callbacks, this might result in an infinite loop if there are more
// callbacks scheduled from within scheduled callbacks
this->callbacksMutex.lock();
while (this->scheduledCallbacks.size() > 0) {
std::cout<<"inside scheduledCallbacks.size() > 0"<<std::endl;
this->callbacksMutex.unlock();
this->runCallbacks();
this->callbacksMutex.lock();
}
this->callbacksMutex.unlock();
}
void LoopingThread::scheduleCallback(std::function<void()> callback) {
std::cout<<"inside schedulecallback"<<std::endl;
this->callbacksMutex.lock();
this->scheduledCallbacks.push_back(callback);
this->callbacksMutex.unlock();
}
void LoopingThread::start() {
if (!this->thread) {
this->thread = new std::thread(&LoopingThread::run, this);
//std::thread::id threadID = std::this_thread::get_id();
//std::cout<<"creating thread: "<<threadID<<std::endl;
}
}
void LoopingThread::join() {
if (this->thread && this->thread->joinable()) {
this->thread->join();
std::cout<<"joining thread"<<std::endl;
}
}
**main.cpp:**
#include <thread>
#include <chrono>
#include <iostream>
#include <mutex>
#include <string>
#include "loopingThread.hpp"
using namespace std;
std::mutex stdoutMutex;
// Example usage of LoopingThread with a classic MainThread:
class MainThread : public LoopingThread {
private:
MainThread();
public:
virtual ~MainThread();
static MainThread& getInstance();
virtual bool tick();
};
MainThread::MainThread() {}
MainThread::~MainThread() {}
MainThread& MainThread::getInstance() {
// Provide a global instance
static MainThread instance;
return instance;
}
bool MainThread::tick() {
// std::cout<<"main thread:"<<threadID<<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
stdoutMutex.lock();
std::cout << "tick" << std::endl;
stdoutMutex.unlock();
// Return false to stop this thread
return true;
}
void doLongAsyncTask() {
std::thread longTask([] () {
stdoutMutex.lock();
std::cout << "Beginning long task..." <<std::endl;
stdoutMutex.unlock();
std::this_thread::sleep_for(std::chrono::seconds(2));
stdoutMutex.lock();
std::cout << "Long task finished!" << std::endl;
stdoutMutex.unlock();
MainThread::getInstance().scheduleCallback([] () {
stdoutMutex.lock();
std::cout << "This is called within the main thread!" << std::endl <<
"No need to worry about thread safety or " <<
"race conditions here" << std::endl;
stdoutMutex.unlock();
});
});
longTask.detach();
}
int main() {
doLongAsyncTask();
MainThread::getInstance().start();
MainThread::getInstance().join();
MainThread::getInstance().run();
}
Now suppose child thread receives any task of creating proxy then It needs to post that task back to the main thread. How to achieve this scenario?

Wait for thread queue to be empty

I am new to C++ and multithreading applications. I want to process a long list of data (potentially several thousands of entries) by dividing its entries among a few threads. I have retrieved a ThreadPool class and a Queue class from the web (it is my first time tackling the subject). I construct the threads and populate the queue in the following way (definitions at the end of the post):
ThreadPool *pool = new ThreadPool(8);
std::vector<std::function<void(int)>> *caller =
new std::vector<std::function<void(int)>>;
for (size_t i = 0; i < Nentries; ++i)
{
caller->push_back(
[=](int j){func(entries[i], j);});
pool->PushTask((*caller)[i]);
}
delete pool;
The problem is that only a number of entries equaling the number of created threads are processed, as if the program does not wait for the queue to be empty. Indeed, if I put
while (pool->GetWorkQueueLength()) {}
just before the pool destructor, the whole list is correctly processed. However, I am afraid I am consuming too many resources by using a while loop. Moreover, I have not found anyone doing anything like it, so I think this is the wrong approach and the classes I use have some error. Can anyone find the error (if present) or suggest another solution?
Here are the classes I use. I suppose the problem is in the implementation of the destructor, but I am not sure.
SynchronizeQueue.hh
#ifndef SYNCQUEUE_H
#define SYNCQUEUE_H
#include <list>
#include <mutex>
#include <condition_variable>
template<typename T>
class SynchronizedQueue
{
public:
SynchronizedQueue();
void Put(T const & data);
T Get();
size_t Size();
private:
SynchronizedQueue(SynchronizedQueue const &)=delete;
SynchronizedQueue & operator=(SynchronizedQueue const &)=delete;
std::list<T> queue;
std::mutex mut;
std::condition_variable condvar;
};
template<typename T>
SynchronizedQueue<T>::SynchronizedQueue()
{}
template<typename T>
void SynchronizedQueue<T>::Put(T const & data)
{
std::unique_lock<std::mutex> lck(mut);
queue.push_back(data);
condvar.notify_one();
}
template<typename T>
T SynchronizedQueue<T>::Get()
{
std::unique_lock<std::mutex> lck(mut);
while (queue.empty())
{
condvar.wait(lck);
}
T result = queue.front();
queue.pop_front();
return result;
}
template<typename T>
size_t SynchronizedQueue<T>::Size()
{
std::unique_lock<std::mutex> lck(mut);
return queue.size();
}
#endif
ThreadPool.hh
#ifndef THREADPOOL_H
#define THREADPOOL_H
#include "SynchronizedQueue.hh"
#include <atomic>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
class ThreadPool
{
public:
ThreadPool(int nThreads = 0);
virtual ~ThreadPool();
void PushTask(std::function<void(int)> func);
size_t GetWorkQueueLength();
private:
void WorkerThread(int i);
std::atomic<bool> done;
unsigned int threadCount;
SynchronizedQueue<std::function<void(int)>> workQueue;
std::vector<std::thread> threads;
};
#endif
ThreadPool.cc
#include "ThreadPool.hh"
#include "SynchronizedQueue.hh"
void doNothing(int i)
{}
ThreadPool::ThreadPool(int nThreads)
: done(false)
{
if (nThreads <= 0)
{
threadCount = std::thread::hardware_concurrency();
}
else
{
threadCount = nThreads;
}
for (unsigned int i = 0; i < threadCount; ++i)
{
threads.push_back(std::thread(&ThreadPool::WorkerThread, this, i));
}
}
ThreadPool::~ThreadPool()
{
done = true;
for (unsigned int i = 0; i < threadCount; ++i)
{
PushTask(&doNothing);
}
for (auto& th : threads)
{
if (th.joinable())
{
th.join();
}
}
}
void ThreadPool::PushTask(std::function<void(int)> func)
{
workQueue.Put(func);
}
void ThreadPool::WorkerThread(int i)
{
while (!done)
{
workQueue.Get()(i);
}
}
size_t ThreadPool::GetWorkQueueLength()
{
return workQueue.Size();
}
You can push tasks saying "done" instead of setting "done" via atomic variable.
So that each thread will exit by itself when seeing "done" task, and no earlier. In destructor you only need to push these tasks and join threads. This is called "poison pill".
Alternatively, if you insist on your current design with done variable, you can wait on the same condition you already have:
std::unique_lock<std::mutex> lck(mut);
while (!queue.empty())
{
condvar.wait(lck);
}
But then you'll need to change your notify_one to notify_all, and this may be sub-optimal.
I want to process a long list of data (potentially several thousands of entries) by dividing its entries among a few threads.
You can do that with parallel algorithms, like tbb::parallel_for:
#include <tbb/parallel_for.h>
#include <vector>
void func(int entry);
int main () {
std::vector<int> entries(1000000);
tbb::parallel_for(size_t{0}, entries.size(), [&](size_t i) { func(entries[i]); });
}
If you need sequential thread ids, you can do:
void func(int element, int thread_id);
template<class C>
inline auto make_range(C& c) -> decltype(tbb::blocked_range<decltype(c.begin())>(c.begin(), c.end())) {
return tbb::blocked_range<decltype(c.begin())>(c.begin(), c.end());
}
int main () {
std::vector<int> entries(1000000);
std::atomic<int> thread_counter{0};
tbb::parallel_for(make_range(entries), [&](auto sub_range) {
static thread_local int const thread_id = thread_counter.fetch_add(1, std::memory_order_relaxed);
for(auto& element : sub_range)
func(element, thread_id);
});
}
Alternatively, there is std::this_thread::get_id.

thread sync using mutex and condition variable

I'm trying to implement an multi-thread job, a producer and a consumer, and basically what I want to do is, when consumer finishes the data, it notifies the producer so that producer provides new data.
The tricky part is, in my current impl, producer and consumer both notifies each other and waits for each other, I don't know how to implement this part correctly.
For example, see the code below,
mutex m;
condition_variable cv;
vector<int> Q; // this is the queue the consumer will consume
vector<int> Q_buf; // this is a buffer Q into which producer will fill new data directly
// consumer
void consume() {
while (1) {
if (Q.size() == 0) { // when consumer finishes data
unique_lock<mutex> lk(m);
// how to notify producer to fill up the Q?
...
cv.wait(lk);
}
// for-loop to process the elems in Q
...
}
}
// producer
void produce() {
while (1) {
// for-loop to fill up Q_buf
...
// once Q_buf is fully filled, wait until consumer asks to give it a full Q
unique_lock<mutex> lk(m);
cv.wait(lk);
Q.swap(Q_buf); // replace the empty Q with the full Q_buf
cv.notify_one();
}
}
I'm not sure this the above code using mutex and condition_variable is the right way to implement my idea,
please give me some advice!
The code incorrectly assumes that vector<int>::size() and vector<int>::swap() are atomic. They are not.
Also, spurious wakeups must be handled by a while loop (or another cv::wait overload).
Fixes:
mutex m;
condition_variable cv;
vector<int> Q;
// consumer
void consume() {
while(1) {
// Get the new elements.
vector<int> new_elements;
{
unique_lock<mutex> lk(m);
while(Q.empty())
cv.wait(lk);
new_elements.swap(Q);
}
// for-loop to process the elems in new_elements
}
}
// producer
void produce() {
while(1) {
vector<int> new_elements;
// for-loop to fill up new_elements
// publish new_elements
{
unique_lock<mutex> lk(m);
Q.insert(Q.end(), new_elements.begin(), new_elements.end());
cv.notify_one();
}
}
}
Maybe that is close to what you want to achive. I used 2 conditional variables to notify producers and consumers between each other and introduced variable denoting which turn is now:
#include <ctime>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
template<typename T>
class ReaderWriter {
private:
std::vector<std::thread> readers;
std::vector<std::thread> writers;
std::condition_variable readerCv, writerCv;
std::queue<T> data;
std::mutex readerMutex, writerMutex;
size_t noReaders, noWriters;
enum class Turn { WRITER_TURN, READER_TURN };
Turn turn;
void reader() {
while (1) {
{
std::unique_lock<std::mutex> lk(readerMutex);
while (turn != Turn::READER_TURN) {
readerCv.wait(lk);
}
std::cout << "Thread : " << std::this_thread::get_id() << " consumed " << data.front() << std::endl;
data.pop();
if (data.empty()) {
turn = Turn::WRITER_TURN;
writerCv.notify_one();
}
}
}
}
void writer() {
while (1) {
{
std::unique_lock<std::mutex> lk(writerMutex);
while (turn != Turn::WRITER_TURN) {
writerCv.wait(lk);
}
srand(time(NULL));
int random_number = std::rand();
data.push(random_number);
std::cout << "Thread : " << std::this_thread::get_id() << " produced " << random_number << std::endl;
turn = Turn::READER_TURN;
}
readerCv.notify_one();
}
}
public:
ReaderWriter(size_t noReadersArg, size_t noWritersArg) : noReaders(noReadersArg), noWriters(noWritersArg), turn(ReaderWriter::Turn::WRITER_TURN) {
}
void run() {
int noReadersArg = noReaders, noWritersArg = noWriters;
while (noReadersArg--) {
readers.emplace_back(&ReaderWriter::reader, this);
}
while (noWritersArg--) {
writers.emplace_back(&ReaderWriter::writer, this);
}
}
~ReaderWriter() {
for (auto& r : readers) {
r.join();
}
for (auto& w : writers) {
w.join();
}
}
};
int main() {
ReaderWriter<int> rw(5, 5);
rw.run();
}
Here's a code snippet. Since the worker treads are already synchronized, requirement of two buffers is ruled out. So a simple queue is used to simulate the scenario:
#include "conio.h"
#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <atomic>
#include <condition_variable>
using namespace std;
enum state_t{ READ = 0, WRITE = 1 };
mutex mu;
condition_variable cv;
atomic<bool> running;
queue<int> buffer;
atomic<state_t> state;
void generate_test_data()
{
const int times = 5;
static int data = 0;
for (int i = 0; i < times; i++) {
data = (data++) % 100;
buffer.push(data);
}
}
void ProducerThread() {
while (running) {
unique_lock<mutex> lock(mu);
cv.wait(lock, []() { return !running || state == WRITE; });
if (!running) return;
generate_test_data(); //producing here
lock.unlock();
//notify consumer to start consuming
state = READ;
cv.notify_one();
}
}
void ConsumerThread() {
while (running) {
unique_lock<mutex> lock(mu);
cv.wait(lock, []() { return !running || state == READ; });
if (!running) return;
while (!buffer.empty()) {
auto data = buffer.front(); //consuming here
buffer.pop();
cout << data << " \n";
}
//notify producer to start producing
if (buffer.empty()) {
state = WRITE;
cv.notify_one();
}
}
}
int main(){
running = true;
thread producer = thread([]() { ProducerThread(); });
thread consumer = thread([]() { ConsumerThread(); });
//simulating gui thread
while (!getch()){
}
running = false;
producer.join();
consumer.join();
}
Not a complete answer, though I think two condition variables could be helpful, one named buffer_empty that the producer thread will wait on, and another named buffer_filled that the consumer thread will wait on. Number of mutexes, how to loop, and so on I cannot comment on, since I'm not sure about the details myself.
Accesses to shared variables should only be done while holding the
mutex that protects it
condition_variable::wait should check a condition.
The condition should be a shared variable protected by the mutex that you pass to condition_variable::wait.
The way to check the condition is to wrap the call to wait in a while loop or use the 2-argument overload of wait (which is
equivalent to the while-loop version)
Note: These rules aren't strictly necessary if you truly understand what the hardware is doing. However, these problems get complicated quickly when with simple data structures, and it will be easier to prove that your algorithm is working correctly if you follow them.
Your Q and Q_buf are shared variables. Due to Rule 1, I would prefer to have them as local variables declared in the function that uses them (consume() and produce(), respectively). There will be 1 shared buffer that will be protected by a mutex. The producer will add to its local buffer. When that buffer is full, it acquires the mutex and pushes the local buffer to the shared buffer. It then waits for the consumer to accept this buffer before producing more data.
The consumer waits for this shared buffer to "arrive", then it acquires the mutex and replaces its empty local buffer with the shared buffer. Then it signals to the producer that the buffer has been accepted so it knows to start producing again.
Semantically, I don't see a reason to use swap over move, since in every case one of the containers is empty anyway. Maybe you want to use swap because you know something about the underlying memory. You can use whichever you want and it will be fast and work the same (at least algorithmically).
This problem can be done with 1 condition variable, but it may be a little easier to think about if you use 2.
Here's what I came up with. Tested on Visual Studio 2017 (15.6.7) and GCC 5.4.0. I don't need to be credited or anything (it's such a simple piece), but legally I have to say that I offer no warranties whatsoever.
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::vector<int> g_deliveryBuffer;
bool g_quit = false;
std::mutex g_mutex; // protects g_deliveryBuffer and g_quit
std::condition_variable g_producerDeliver;
std::condition_variable g_consumerAccepted;
// consumer
void consume()
{
// local buffer
std::vector<int> consumerBuffer;
while (true)
{
if (consumerBuffer.empty())
{
std::unique_lock<std::mutex> lock(g_mutex);
while (g_deliveryBuffer.empty() && !g_quit) // if we beat the producer, wait for them to push to the deliverybuffer
g_producerDeliver.wait(lock);
if (g_quit)
break;
consumerBuffer = std::move(g_deliveryBuffer); // get the buffer
}
g_consumerAccepted.notify_one(); // notify the producer that the buffer has been accepted
// for-loop to process the elems in Q
// ...
consumerBuffer.clear();
// ...
}
}
// producer
void produce()
{
std::vector<int> producerBuffer;
while (true)
{
// for-loop to fill up Q_buf
// ...
producerBuffer = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// ...
// once Q_buf is fully filled, wait until consumer asks to give it a full Q
{ // scope is for lock
std::unique_lock<std::mutex> lock(g_mutex);
g_deliveryBuffer = std::move(producerBuffer); // ok to push to deliverybuffer. it is guaranteed to be empty
g_producerDeliver.notify_one();
while (!g_deliveryBuffer.empty() && !g_quit)
g_consumerAccepted.wait(lock); // wait for consumer to signal for more data
if (g_quit)
break;
// We will never reach this point if the buffer is not empty.
}
}
}
int main()
{
// spawn threads
std::thread consumerThread(consume);
std::thread producerThread(produce);
// for for 5 seconds
std::this_thread::sleep_for(std::chrono::seconds(5));
// signal that it's time to quit
{
std::lock_guard<std::mutex> lock(g_mutex);
g_quit = true;
}
// one of the threads may be sleeping
g_consumerAccepted.notify_one();
g_producerDeliver.notify_one();
consumerThread.join();
producerThread.join();
return 0;
}

How to prematurely kill std::async threads before they are finished *without* using a std::atomic_bool?

I have a function that takes a callback, and used it to do work on 10 separate threads. However, it is often the case that not all of the work is needed. For example, if the desired result is obtained on the third thread, it should stop all work being done on of the remaining alive threads.
This answer here suggests that it is not possible unless you have the callback functions take an additional std::atomic_bool argument, that signals whether the function should terminate prematurely.
This solution does not work for me. The workers are spun up inside a base class, and the whole point of this base class is to abstract away details of multithreading. How can I do this? I am anticipating that I will have to ditch std::async for something more involved.
#include <iostream>
#include <future>
#include <vector>
class ABC{
public:
std::vector<std::future<int> > m_results;
ABC() {};
~ABC(){};
virtual int callback(int a) = 0;
void doStuffWithCallBack();
};
void ABC::doStuffWithCallBack(){
// start working
for(int i = 0; i < 10; ++i)
m_results.push_back(std::async(&ABC::callback, this, i));
// analyze results and cancel all threads when you get the 1
for(int j = 0; j < 10; ++j){
double foo = m_results[j].get();
if ( foo == 1){
break; // but threads continue running
}
}
std::cout << m_results[9].get() << " <- this shouldn't have ever been computed\n";
}
class Derived : public ABC {
public:
Derived() : ABC() {};
~Derived() {};
int callback(int a){
std::cout << a << "!\n";
if (a == 3)
return 1;
else
return 0;
};
};
int main(int argc, char **argv)
{
Derived myObj;
myObj.doStuffWithCallBack();
return 0;
}
I'll just say that this should probably not be a part of a 'normal' program, since it could leak resources and/or leave your program in an unstable state, but in the interest of science...
If you have control of the thread loop, and you don't mind using platform features, you could inject an exception into the thread. With posix you can use signals for this, on Windows you would have to use SetThreadContext(). Though the exception will generally unwind the stack and call destructors, your thread may be in a system call or other 'non-exception safe place' when the exception occurs.
Disclaimer: I only have Linux at the moment, so I did not test the Windows code.
#if defined(_WIN32)
# define ITS_WINDOWS
#else
# define ITS_POSIX
#endif
#if defined(ITS_POSIX)
#include <signal.h>
#endif
void throw_exception() throw(std::string())
{
throw std::string();
}
void init_exceptions()
{
volatile int i = 0;
if (i)
throw_exception();
}
bool abort_thread(std::thread &t)
{
#if defined(ITS_WINDOWS)
bool bSuccess = false;
HANDLE h = t.native_handle();
if (INVALID_HANDLE_VALUE == h)
return false;
if (INFINITE == SuspendThread(h))
return false;
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_CONTROL;
if (GetThreadContext(h, &ctx))
{
#if defined( _WIN64 )
ctx.Rip = (DWORD)(DWORD_PTR)throw_exception;
#else
ctx.Eip = (DWORD)(DWORD_PTR)throw_exception;
#endif
bSuccess = SetThreadContext(h, &ctx) ? true : false;
}
ResumeThread(h);
return bSuccess;
#elif defined(ITS_POSIX)
pthread_kill(t.native_handle(), SIGUSR2);
#endif
return false;
}
#if defined(ITS_POSIX)
void worker_thread_sig(int sig)
{
if(SIGUSR2 == sig)
throw std::string();
}
#endif
void init_threads()
{
#if defined(ITS_POSIX)
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = worker_thread_sig;
sigaction(SIGUSR2, &sa, 0);
#endif
}
class tracker
{
public:
tracker() { printf("tracker()\n"); }
~tracker() { printf("~tracker()\n"); }
};
int main(int argc, char *argv[])
{
init_threads();
printf("main: starting thread...\n");
std::thread t([]()
{
try
{
tracker a;
init_exceptions();
printf("thread: started...\n");
std::this_thread::sleep_for(std::chrono::minutes(1000));
printf("thread: stopping...\n");
}
catch(std::string s)
{
printf("thread: exception caught...\n");
}
});
printf("main: sleeping...\n");
std::this_thread::sleep_for(std::chrono::seconds(2));
printf("main: aborting...\n");
abort_thread(t);
printf("main: joining...\n");
t.join();
printf("main: exiting...\n");
return 0;
}
Output:
main: starting thread...
main: sleeping...
tracker()
thread: started...
main: aborting...
main: joining...
~tracker()
thread: exception caught...
main: exiting...

Multiple consumer and producers, how do I unlock consumer thread

I am trying to make a multiple producer and consumer program. The producers produce random numbers and insert them into a shared queue(shared memory) and the consumers print out the numbers. The user calls the program with the following arguments: number of producer threads, number of consumer threads and the size of the shared data.
Right now it just produces one producer(it seems) and just stops. I wanted to see if I can get some help figuring out how to unlock the consumers.
This is the Queue header
class SyncQueue
{
public:
SyncQueue(int sizeMax);
void enqueue(int value);
int dequeue();
private:
int MaxSize, front, rear, itemcounter;
std::vector<int> QueueElements;
std::mutex mutex;
//Condition variables for full and empty checks
std::condition_variable NotFull;
std::condition_variable NotEmpty;
};
This is the Queue functions
SyncQueue::SyncQueue(int sizeMax)
{
front = 0;
rear = 0;
MaxSize = sizeMax;
itemcounter = 0;
QueueElements.reserve(MaxSize);
}
void SyncQueue::enqueue(int value)
{
std::unique_lock<std::mutex> lock(mutex);
NotFull.wait(lock , [this](){return itemcounter != MaxSize; });
QueueElements[rear] = value;
rear = (rear + 1) % MaxSize;
++itemcounter;
NotEmpty.notify_all();
}
int SyncQueue::dequeue()
{
std::unique_lock<std::mutex> lock(mutex);
NotEmpty.wait(lock, [this](){return itemcounter != 0; });
int number = QueueElements[front];
front = (front + 1) % MaxSize;
--itemcounter;
NotFull.notify_all();
return number;
}
This is main where I create the threads
std::vector<std::thread> producers(producerThreadCount);
std::vector<std::thread> consumers(consumerThreadCount);
SyncQueue queue(size);
//Build producer threads
for (int i = 0; i < producerThreadCount; i++)
{
producers[i] = std::thread(produceThread, i,std::ref(ProducerMutex), std::ref(queue), 200);
}
//Build consumers
for (int i = 0; i < consumerThreadCount; i++)
{
consumers[i] = std::thread(consumeThread, i, std::ref(ConsumerMutex), std::ref(queue), 400);
}
These are the produce and consume threads
void produceThread(int threadId, std::mutex &ProducerMutex, SyncQueue &sharedQueue, int time)
{
while (true)
{
int value = RandomNumberGenerator(std::ref(ProducerMutex));
sharedQueue.enqueue(value);
std::this_thread::sleep_for(std::chrono::milliseconds(time));
}
}
void consumeThread(int threadId, std::mutex &ConsumerMutex, SyncQueue &sharedQueue, int time)
{
while (true)
{
std::lock_guard<std::mutex> lock(ConsumerMutex);
int value;
std::cout << "Thread:" << threadId << " consumes:" <<sharedQueue.dequeue() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(time));
}
}

Resources