Multithreaded Producer/Consumer in C++ - multithreading

I am looking at multithreading and written a basic producer/consumer. I have two issues with the producer/consumer written below. 1) Even by setting the consumer sleep time lower than the producer sleep time, the producer still seems to execute quicker. 2) In the consumer I have duplicated the code in the case where the producer finishes adding to the queue, but there is still elements in the queue. Any advise for a better way of structuring the code?
#include <iostream>
#include <queue>
#include <mutex>
class App {
private:
std::queue<int> m_data;
bool m_bFinished;
std::mutex m_Mutex;
int m_ConsumerSleep;
int m_ProducerSleep;
int m_QueueSize;
public:
App(int &MaxQueue) :m_bFinished(false), m_ConsumerSleep(1), m_ProducerSleep(5), m_QueueSize(MaxQueue){}
void Producer() {
for (int i = 0; i < m_QueueSize; ++i) {
std::lock_guard<std::mutex> guard(m_Mutex);
m_data.push(i);
std::cout << "Producer Thread, queue size: " << m_data.size() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(m_ProducerSleep));
}
m_bFinished = true;
}
void Consumer() {
while (!m_bFinished) {
if (m_data.size() > 0) {
std::lock_guard<std::mutex> guard(m_Mutex);
std::cout << "Consumer Thread, queue element: " << m_data.front() << " size: " << m_data.size() << std::endl;
m_data.pop();
}
else {
std::cout << "No elements, skipping" << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(m_ConsumerSleep));
}
while (m_data.size() > 0) {
std::lock_guard<std::mutex> guard(m_Mutex);
std::cout << "Emptying remaining elements " << m_data.front() << std::endl;
m_data.pop();
std::this_thread::sleep_for(std::chrono::seconds(m_ConsumerSleep));
}
}
};
int main()
{
int QueueElements = 10;
App app(QueueElements);
std::thread consumer_thread(&App::Consumer, &app);
std::thread producer_thread(&App::Producer, &app);
producer_thread.join();
consumer_thread.join();
std::cout << "loop exited" << std::endl;
return 0;
}

You should use condition_variable. Don't use sleep for threads.
Main scheme:
Producer pushes value under lock and signals condition_variable.
Consumer waits under lock on condition variable and checks predicate to prevent spurious wakeups.
My version:
#include <iostream>
#include <queue>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <atomic>
class App {
private:
std::queue<int> m_data;
std::atomic_bool m_bFinished;
std::mutex m_Mutex;
std::condition_variable m_cv;
int m_QueueSize;
public:
App(int MaxQueue)
: m_bFinished(false)
, m_QueueSize(MaxQueue)
{}
void Producer()
{
for (int i = 0; i < m_QueueSize; ++i)
{
{
std::unique_lock<std::mutex> lock(m_Mutex);
m_data.push(i);
}
m_cv.notify_one();
std::cout << "Producer Thread, queue size: " << m_data.size() << std::endl;
}
m_bFinished = true;
}
void Consumer()
{
do
{
std::unique_lock<std::mutex> lock(m_Mutex);
while (m_data.empty())
{
m_cv.wait(lock, [&](){ return !m_data.empty(); }); // predicate an while loop - protection from spurious wakeups
}
while(!m_data.empty()) // consume all elements from queue
{
std::cout << "Consumer Thread, queue element: " << m_data.front() << " size: " << m_data.size() << std::endl;
m_data.pop();
}
} while(!m_bFinished);
}
};
int main()
{
int QueueElements = 10;
App app(QueueElements);
std::thread consumer_thread(&App::Consumer, &app);
std::thread producer_thread(&App::Producer, &app);
producer_thread.join();
consumer_thread.join();
std::cout << "loop exited" << std::endl;
return 0;
}
Also note, that it's better to use atomic for end flag, when you have deal with concurrent threads, because theoretically value of the m_bFinished will be stored in the cache-line and if there is no cache invalidation in the producer thread, the changed value can be unseen from the consumer thread. Atomics have memory fences, that guarantees, that value will be updated for other threads.
Also you can take a look on memory_order page.

First, you should use a condition variable instead of a delay on the consumer. This way, the consumer thread only wakes up when the queue is not empty and the producer notifies it.
That said, the reason why your producer calls are more frequent is the delay on the producer thread. It's executed while holding the mutex, so the consumer will never execute until the delay is over. You should release the mutex before calling sleep_for:
for (int i = 0; i < m_QueueSize; ++i) {
/* Introduce a scope to release the mutex before sleeping*/
{
std::lock_guard<std::mutex> guard(m_Mutex);
m_data.push(i);
std::cout << "Producer Thread, queue size: " << m_data.size() << std::endl;
} // Mutex is released here
std::this_thread::sleep_for(std::chrono::seconds(m_ProducerSleep));
}

Related

Object address suddenly changed

class test
{
void thread1()
{
int i = 0;
while(true){
for(unsigned int k = 0;k < mLD.size(); k++ )
{
mLD[k] = i++;
}
}
}
void thread2()
{
std::cout << "thread2 address : " << &mLD << "\n";
C();
}
void B()
{
std::cout << "B address : " << &mLD << "\n";
for(unsigned int k = 0;k < mLD.size(); k++ )
{
if(mLD[k]<=25)
{
}
}
}
void C()
{
B();
std::cout << "C address : " << &mLD << "\n";
double distance = mLD[0]; // <---- segmetation fault
}
std::array<double, 360> mLD;
};
cout result --->
thread2 address : 0x7e807660
B address : 0x7e807660
C address : 0x1010160 (sometimes 0x7e807660 )
Why mLD's address changed ....?
even i change std::array to std::array<std::atomic<double>360>, the result is the same.
Most probably, the object you referred is destroyed at the point of call to C, which points to a synchronization issue. You need to extend the lifetime of the object referred by thread(s), until the threads done executing their routine. To accomplish this, you can have something like this;
#include <thread>
#include <array>
#include <iostream>
struct foo{
void callback1(){
for(auto & elem: storage){
elem += 5;
}
}
void callback2(){
for(const auto & elem: storage){
std::cout << elem << std::endl;
}
}
std::array<double, 300> storage;
};
int main(void){
foo f;
std::thread t1 {[&f](){f.callback1();}};
std::thread t2 {[&f](){f.callback2();}};
// wait until both threads are done executing their routines
t1.join();
t2.join();
return 0;
}
The instance of foo, f lives in scope of main() function, so its' lifetime is defined by from the line it defined to end of the main's scope. By joining both threads, we block main from proceeding further until both threads are done executing their callback functions, hence the lifetime of f extended until callbacks are done.
The second issue is, the code needs synchronization primitives, because storage variable is shared between two independent execution paths. The final code with proper synchronization can look like this;
#include <thread>
#include <array>
#include <iostream>
#include <mutex>
struct foo{
void callback1(){
// RAII style lock, which invokes .lock() upon construction, and .unlock() upon destruction
// automatically.
std::unique_lock<std::mutex> lock(mtx);
for(auto & elem: storage){
elem += 5;
}
}
void callback2(){
std::unique_lock<std::mutex> lock(mtx);
for(const auto & elem: storage){
std::cout << elem << std::endl;
}
}
std::array<double, 300> storage;
// non-reentrant mutex
mutable std::mutex mtx;
};
int main(void){
foo f;
std::thread t1 {[&f](){f.callback1();}};
std::thread t2 {[&f](){f.callback2();}};
// wait until both threads are done executing their routines
t1.join();
t2.join();
return 0;
}

Is there an alternative way to sync

Is there an alternative way to be sure that the threads are ready to recieve the broadcast signal. I want to replace the Sleep(1) function in main.
#include <iostream>
#include <pthread.h>
#define NUM 4
using namespace std;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_t tid[NUM];
void *threads(void *arg){
int tid = (int)arg;
while(true){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
//do some work
cout<<"Thread: "<<tid<<endl;;
pthread_mutex_unlock(&mutex);
}
}
int main(){
for(int i=0;i<NUM;i++){
pthread_create(&tid[i],NULL,threads,(void*)i);
}
Sleep(1);
pthread_cond_broadcast(&cond);
Sleep(1);
pthread_cond_broadcast(&cond);
Sleep(1);
pthread_cond_broadcast(&cond);
return 0;
}
I tried memory barriers before pthread_cond_wait and i thought of using an counter, but nothing worked for me yet.
Condition variables are usually connected to a predicate. In the other threads, check if predicate is already fulfilled (check while holding the mutex protecting the predicate), if so, do not wait on the condition variable. In main, acquire mutex, change predicate while holding the mutex. Then release mutex and signal or broadcast on the condvar. Here is a similar question:
Synchronisation before pthread_cond_broadcast
Here is some example code:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cassert>
#define NUM 4
#define SIZE 256
using std::cout;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_t tid[NUM];
int work_available;
void *threads(void *arg)
{
int tid = *((int*)arg);
while (1) {
pthread_mutex_lock(&mutex);
while (work_available == 0) {
// While loop since cond_wait can have spurious wakeups.
pthread_cond_wait(&cond, &mutex);
cout << "Worker " << tid << " woke up...\n";
cout << "Work available: " << work_available << '\n';
}
if (work_available == -1) {
cout << "Worker " << tid << " quitting\n";
pthread_mutex_unlock(&mutex); // Easy to forget, better to use C++11 RAII mutexes.
break;
}
assert(work_available > 0);
work_available--;
cout << "Worker " << tid << " took one item of work\n";
pthread_mutex_unlock(&mutex);
//do some work
sleep(2); // simulated work
pthread_mutex_lock(&mutex);
cout << "Worker " << tid << " done with one item of work.\n";
pthread_mutex_unlock(&mutex);
}
}
int main()
{
work_available = 0;
int args[NUM];
for (int i=0; i<NUM; i++) {
args[i] = i;
pthread_create(&tid[i], NULL, threads, (void*)&args[i]);
}
const int MAX_TIME = 10;
for (int i = 0; i < MAX_TIME; i++)
{
pthread_mutex_lock(&mutex);
work_available++;
cout << "Main thread, work available: " << work_available << '\n';
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
sleep(1);
}
pthread_mutex_lock(&mutex);
cout << "Main signalling threads to quit\n";
work_available = -1;
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
for (int i = 0; i < NUM; i++)
{
pthread_join(tid[i], NULL);
}
return 0;
}

Can't exit exec loop in Qt

Well, I have created a program which picks up the input signal from serial input. I can successfully receive the data transmitted from the device through UART. I want to terminate the thread after achieving certain conditions( such as receiving more than 5 bytes, etc.) I think the problem is how to terminate the thread in Qt correctly, but I couldn't find the way. The program seems falls into deadlock after calling the exec() in the sub function. Can anyone help with that problem? Thank you very much!
Here's my header file:
#ifndef SERIALTHREAD
#define SERIALTHREAD
#include <QtSerialPort/QSerialPort>
#include <QDebug>
#include <QString>
#include <QThread>
#include <QtCore>
#include <iostream>
#include <fstream>
class SerialControlThread : public QThread
{
Q_OBJECT
public:
explicit SerialControlThread(QString ComPort,QObject *parent = 0);
~SerialControlThread(); // Destructor
bool openSerialPort();
void closeSerialPort();
void run();
bool TelltoExit();
void StarttoRun();
private:
int DataCount;
QString ComPortNumber;
QSerialPort *serial;
int* VoltageStorage; // Total 3 channels, each channel takes 10 data
unsigned int Channel_A[10]; // Channel_A is for Phase Tx s
int DataCountIndexA; // This is how many data has been sent to the buffer;
int SentDataCount;
unsigned char StoreDataBuffer[2];
unsigned char TotalDataCounter;
std::ofstream write;
signals:
void BufferisFull(int*);
void TimeToQuit();
public slots:
private slots:
void readData();
void handleError(QSerialPort::SerialPortError error);
};
#endif // SERIALTHREAD
This is the.cpp
#include "serialcontrol.h"
#include <iostream>
SerialControlThread::SerialControlThread(QString ComPort,QObject *parent) :
QThread(parent),ComPortNumber(ComPort)
{
DataCountIndexA=0;
DataCount=0;
serial = new QSerialPort(this);
connect(this,SIGNAL(TimeToQuit()),this,SLOT(quit()));\
connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
SLOT(handleError(QSerialPort::SerialPortError)));
for (int i=0;i<10;i++)
Channel_A[i]=0;
}
SerialControlThread::~SerialControlThread()
{
this->closeSerialPort();
delete serial;
}
bool SerialControlThread::openSerialPort()
{
// std::cout << "Hey I am in serial function" << std::endl;
serial->setPortName(ComPortNumber) ;
serial->setBaudRate(QSerialPort::Baud9600); //This can be set through menu in the future
serial->setDataBits(QSerialPort::Data8); // A packets contains 8 bits ( 3 for signature bits)
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
if (!(serial->open(QIODevice::ReadWrite))) {
return false; // return false when the device can't be opened
}else
{
return true;} // return true when the device is avalaible
}
void SerialControlThread::closeSerialPort()
{
if (serial->isOpen())
serial->close();
}
void SerialControlThread::handleError(QSerialPort::SerialPortError error)
{
}
void SerialControlThread::readData()
{
QByteArray data=serial->read(100);
const char *TempChar=data.data();
std::cout << TempChar << std::endl;
DataCount++;
if(DataCount>=4)
{
std::cout << "I am bigger than 4" << std::endl;
this->quit();
}
}
}
void SerialControlThread::run()
{
}
bool SerialControlThread::TelltoExit()
{
}
void SerialControlThread::StarttoRun()
{
// Sending the msp430 S to activate the following sequence
const char *temp="S";
serial->write(temp);
serial->waitForBytesWritten(30000);
this->exec();
}
This is the main.cpp
#include <QCoreApplication>
#include <QtSerialPort/QSerialPortInfo>
#include <QList>
#include <iostream>
#include <QString>
#include <QDebug>
#include <QSerialPort>
#include "serialcontrol.h"
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int AvailablePorts=QSerialPortInfo::availablePorts().count();
QList<QSerialPortInfo> SerialObject=QSerialPortInfo::availablePorts();
cout << "There are total: " << SerialObject.count() << " available ports " << endl << endl;
QString description;
for (int i=0;i<AvailablePorts;i++)
{
cout << "The " << i+1 << " com port is :";
qDebug() << SerialObject[i].portName();
qDebug() << "Description : " << SerialObject[i].description();
qDebug() << "Manufacturer: " << SerialObject[i].manufacturer();
cout << endl;
}
SerialControlThread *RunThread=new SerialControlThread(SerialObject[0].portName(),&a);
cout << RunThread->openSerialPort() << endl;
RunThread->StarttoRun();
cout << "I am out of here" << endl;
delete RunThread;
return a.exec();
}
I wish to close the thread( back to the main function) when the buffer has received more than 4 data, but it doesn't.
It is my output
There are total: 1 available ports
The 1 com port is :"COM8"
Description : "MSP430 Application UART"
Manufacturer: "Texas Instruments"
1
0
1
2
3
I am bigger than 4
4
I am bigger than 4
5
I am bigger than 4
6
I am bigger than 4
7
I am bigger than 4
8
I am bigger than 4
9
I am bigger than 4
Apparently, the program gets stuck in a loop. I have tried some solutions, but none of these worked.
StartToRun calls QThread::exec in the wrong thread: you call it in the main thread, but it's supposed to be called in the thread itself - from within run().
Alas, SerialControlThread doesn't have to be a thread. Making it a thread forces it to be used in a dedicated thread - that should be a choice left to its user. Perhaps the thread would be shared among other serial controllers, or perhaps it'll do just fine in the main thread. Thus, it should be an object that handles serial data, that has a thread-safe interface so that you can move it to another thread if you wish - but would still work fine in the main thread, and thus has to handle data asynchronously without blocking.
Considering whether one needs to control the worker thread's run status so tightly: an idle thread consumes no resources - its event loop is blocked waiting on new events, its stack eventually gets paged out if there's memory pressure. If one intends to "wake" the thread for each operation, there's no need to be explicit about it: the event loop in the thread behaves that way be default and by design: it wakes when there are new events, such as incoming data, otherwise it sleeps. One shouldn't be stopping the thread then.
The example below shows a very minimal implementation. On the whole it's not very useful other than to demonstrate brevity as a contrast to the length of code in the question - in spite of identical limited functionality. Presumably you have a more complex communications protocol that you wish to handle. You may wish to consider the use of QDataStream read transactions to make the reader code more expressive, and using a state machine to represent the protocol.
// https://github.com/KubaO/stackoverflown/tree/master/questions/serial-galore-42241570
#include <QtWidgets>
#include <QtSerialPort>
// See https://stackoverflow.com/q/40382820/1329652
template <typename Fun> void safe(QObject * obj, Fun && fun) {
Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
if (Q_LIKELY(obj->thread() == QThread::currentThread() || !obj->thread()))
return fun();
struct Event : public QEvent {
using F = typename std::decay<Fun>::type;
F fun;
Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
~Event() { fun(); }
};
QCoreApplication::postEvent(
obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
}
class SerialController : public QObject {
Q_OBJECT
QSerialPort m_port{this};
QByteArray m_rxData;
void onError(QSerialPort::SerialPortError error) {
Q_UNUSED(error);
}
void onData(const QByteArray & data) {
m_rxData.append(data);
qDebug() << "Got" << m_rxData.toHex() << "(" << m_rxData.size() << ") - done.";
emit hasReply(m_rxData);
}
void onData() {
if (m_port.bytesAvailable() >= 4)
onData(m_port.readAll());
}
public:
explicit SerialController(const QString & port, QObject * parent = nullptr) :
QObject{parent}
{
m_port.setPortName(port);
connect(&m_port, static_cast<void(QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
this, &SerialController::onError);
}
~SerialController() { qDebug() << __FUNCTION__; }
bool open() {
m_port.setBaudRate(QSerialPort::Baud9600);
m_port.setDataBits(QSerialPort::Data8);
m_port.setParity(QSerialPort::NoParity);
m_port.setStopBits(QSerialPort::OneStop);
m_port.setFlowControl(QSerialPort::NoFlowControl);
return m_port.open(QIODevice::ReadWrite);
}
/// This method is thread-safe.
void start() {
safe(this, [=]{
m_port.write("S");
qDebug() << "Sent data";
});
}
Q_SIGNAL void hasReply(const QByteArray &);
void injectData(const QByteArray & data) {
onData(data);
}
};
QDebug operator<<(QDebug dbg, const QSerialPortInfo & info) {
dbg << info.portName();
if (!info.description().isEmpty())
dbg << " Description: " << info.description();
if (!info.manufacturer().isEmpty())
dbg << " Manufacturer: " << info.manufacturer();
return dbg;
}
// A thread that starts on construction, and is always safe to destruct.
class RunningThread : public QThread {
Q_OBJECT
using QThread::run; // final
public:
RunningThread(QObject * parent = nullptr) : QThread(parent) { start(); }
~RunningThread() { qDebug() << __FUNCTION__; quit(); wait(); }
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
auto const ports = QSerialPortInfo::availablePorts();
if (ports.isEmpty())
qFatal("No serial ports");
int n{};
qDebug() << "Available ports:";
for (auto & port : ports)
qDebug() << "port[" << n++ << "]: " << port;
SerialController ctl{ports.at(5).portName()};
if (!ctl.open())
qFatal("Open Failed");
// Optional: the controller will work fine in the main thread.
if (true) ctl.moveToThread(new RunningThread{&ctl}); // Owns its thread
// Let's pretend we got a reply;
QTimer::singleShot(1000, &ctl, [&ctl]{
ctl.injectData("ABCD");
});
QObject::connect(&ctl, &SerialController::hasReply, ctl.thread(), &QThread::quit);
QObject::connect(&ctl, &SerialController::hasReply, [&]{
qDebug() << "The controller is done, quitting.";
app.quit();
});
ctl.start();
return app.exec();
}
#include "main.moc"

C++11 non-blocking, long-running, producer consumer threads

I am trying to teach myself C++11 threading, and I would like to start a background producer thread at the beginning of the application, and have it run until application exit. I would also like to have consumer thread (which also runs for the life of the application).
A real-world example would be a producer thread listening on a Com port for incoming GPS data. Once a full message had been accumulated, it could be parsed to see if it was a message of interest, then converted into a string (say), and 'delivered back' to be consumed (update current location, for example).
My issue is I haven't been able to figure out how to do this without blocking the rest of the application when I 'join()' on the consumer thread.
Here is my very simplified example that hopefully shows my issues:
#include <QCoreApplication>
#include <QDebug>
#include <thread>
#include <atomic>
#include <iostream>
#include <queue>
#include <mutex>
#include <chrono>
#include "threadsafequeuetwo.h"
ThreadSafeQueueTwo<int> goods;
std::mutex mainMutex;
std::atomic<bool> isApplicationRunning = false;
void theProducer ()
{
std::atomic<int> itr = 0;
while(isApplicationRunning)
{
// Simulate this taking some time...
std::this_thread::sleep_for(std::chrono::milliseconds(60));
// Push the "produced" value onto the queue...
goods.push(++itr);
// Diagnostic printout only...
if ((itr % 10) == 0)
{
std::unique_lock<std::mutex> lock(mainMutex);
std::cout << "PUSH " << itr << " on thread ID: "
<< std::this_thread::get_id() << std::endl;
}
// Thread ending logic.
if (itr > 100) isApplicationRunning = false;
}
}
void theConsumer ()
{
while(isApplicationRunning || !goods.empty())
{
int val;
// Wait on new values, and 'pop' when available...
goods.waitAndPop(val);
// Here, we would 'do something' with the new values...
// Simulate this taking some time...
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// Diagnostic printout only...
if ((val % 10) == 0)
{
std::unique_lock<std::mutex> lock(mainMutex);
std::cout << "POP " << val << " on thread ID: "
<< std::this_thread::get_id() << std::endl;
}
}
}
int main(int argc, char *argv[])
{
std::cout << "MAIN running on thread ID: "
<< std::this_thread::get_id() << std::endl;
// This varaiable gets set to true at startup, and,
// would only get set to false when the application
// wants to exit.
isApplicationRunning = true;
std::thread producerThread (theProducer);
std::thread consumerThread (theConsumer);
producerThread.detach();
consumerThread.join(); // BLOCKS!!! - how to get around this???
std::cout << "MAIN ending on thread ID: "
<< std::this_thread::get_id() << std::endl;
}
The ThreadSafeQueueTwo class is the thread safe queue implementation taken almost exactly as is from the "C++ Concurrency In Action" book. This seems to work just fine. Here it is if anybody is interested:
#ifndef THREADSAFEQUEUETWO_H
#define THREADSAFEQUEUETWO_H
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>
template<typename T>
class ThreadSafeQueueTwo
{
public:
ThreadSafeQueueTwo()
{}
ThreadSafeQueueTwo(ThreadSafeQueueTwo const& rhs)
{
std::lock_guard<std::mutex> lock(myMutex);
myQueue = rhs.myQueue;
}
void push(T newValue)
{
std::lock_guard<std::mutex> lock(myMutex);
myQueue.push(newValue);
myCondVar.notify_one();
}
void waitAndPop(T& value)
{
std::unique_lock<std::mutex> lock(myMutex);
myCondVar.wait(lock, [this]{return !myQueue.empty(); });
value = myQueue.front();
myQueue.pop();
}
std::shared_ptr<T> waitAndPop()
{
std::unique_lock<std::mutex> lock(myMutex);
myCondVar.wait(lock, [this]{return !myQueue.empty(); });
std::shared_ptr<T> sharedPtrToT (std::make_shared<T>(myQueue.front()));
myQueue.pop();
return sharedPtrToT;
}
bool tryPop(T& value)
{
std::lock_guard<std::mutex> lock(myMutex);
if (myQueue.empty())
return false;
value = myQueue.front();
myQueue.pop();
return true;
}
std::shared_ptr<T> tryPop()
{
std::lock_guard<std::mutex> lock(myMutex);
if (myQueue.empty())
return std::shared_ptr<T>();
std::shared_ptr<T> sharedPtrToT (std::make_shared<T>(myQueue.front()));
myQueue.pop();
return sharedPtrToT;
}
bool empty()
{
std::lock_guard<std::mutex> lock(myMutex);
return myQueue.empty();
}
private:
mutable std::mutex myMutex;
std::queue<T> myQueue;
std::condition_variable myCondVar;
};
#endif // THREADSAFEQUEUETWO_H
Here's the output:
I know there are obvious issues with my example, but my main question is how would I run something like this in the background, without blocking the main thread?
Perhaps an even better way of trying to solve this is, is there a way that every time the producer has 'produced' some new data, could I simply call a method in the main thread, passing in the new data? This would be similar to queued signal/slots it Qt, which I am big fan of.

Making changes to future object

I am trying to change the behavior of a future object based on user input.
#include <iostream>
#include <future>
//=======================================================================================!
struct DoWork
{
DoWork(int cycles, int restTime) : _cycles(cycles), _restTime(restTime), _stop(false)
{
}
void operator () ()
{
for(int i = 0 ; i < _cycles; ++i)
{
std::this_thread::sleep_for(std::chrono::milliseconds(_restTime));
if(_stop)break;
doTask();
}
}
void stop()
{
_stop = true;
}
private:
void doTask()
{
std::cout << "doing task!" << std::endl;
}
private:
int _cycles;
int _restTime;
bool _stop;
};
//=======================================================================================!
int main()
{
DoWork doObj(50, 500);
std::future<int> f = std::async(std::launch::async, doObj);
std::cout << "Should I stop work ?" << std::endl;
std::cout << "('1' = Yes, '2' = no, 'any other' = maybe)" << std::endl;
int answer;
std::cin >> answer;
if(answer == 1) doObj.stop();
std::cout << f.get() << std::endl;
return 0;
}
//=======================================================================================!
However this does not stop the execution of the future object. How do I change the behavior of the doObj after I have created the future object?
You have a few problems. First, your function object doesn't actually return int, so std::async will return a std::future<void>. You can fix this either by actually returning int from DoWork::operator(), or by storing the result from async in a std::future<void> and not trying to print it.
Second, std::async copies its arguments if they aren't in reference wrappers, so the doObj on the stack is not going to be the same instance of DoWork that is being used by the asynchronous thread. You can correct this by passing doObj in a reference wrapper a la std::async(std::launch::async, std::ref(doObj)).
Third, both the main thread and the asynchronous thread are simultaneously accessing DoWork::_stop. This is a data race and means the program has undefined behavior. The fix is to protect accesses to _stop with a std::mutex or to make it a std::atomic.
Altogether, program should look like (Live at Coliru):
#include <iostream>
#include <future>
//=======================================================================================!
struct DoWork
{
DoWork(int cycles, int restTime) : _cycles(cycles), _restTime(restTime), _stop(false)
{
}
int operator () ()
{
for(int i = 0 ; i < _cycles; ++i)
{
std::this_thread::sleep_for(std::chrono::milliseconds(_restTime));
if(_stop) return 42;
doTask();
}
return 13;
}
void stop()
{
_stop = true;
}
private:
void doTask()
{
std::cout << "doing task!" << std::endl;
}
private:
int _cycles;
int _restTime;
std::atomic<bool> _stop;
};
//=======================================================================================!
int main()
{
DoWork doObj(50, 500);
std::future<int> f = std::async(std::launch::async, std::ref(doObj));
std::cout << "Should I stop work ?" << std::endl;
std::cout << "('1' = Yes, '2' = no, 'any other' = maybe)" << std::endl;
int answer;
std::cin >> answer;
if(answer == 1) doObj.stop();
std::cout << f.get() << std::endl;
}
//=======================================================================================!

Resources