I wonder whether I can use the side effects of a conditional_variable test?
Is it guaranteed that the conditional_variable test is returning to execution if it returns true, or can there be the situation that the test returns
true, but it is called again or times out in between?
In the below example maybeCmd_locked() de-queues a cmd, however I want to
avoid that it is called 2 times for one exit of the conditional_variable wait:
if (cv.wait_until(lk, now + 100ms, [&cmd,this]{ return ((cmd = maybeCmd_locked()) != -1); }))
//g++ test.cpp -o test.exe -lstdc++ -lpthread
#include <stdlib.h>
#include <stdio.h>
#include <thread>
#include <queue>
#include <chrono>
#include <mutex>
#include <condition_variable>
using namespace std::literals::chrono_literals;
class eventLooper {
public:
eventLooper() : threadexit(false) {};
bool threadexit;
std::queue<int> cmds;
std::mutex m;
std::condition_variable cv;;
int maybeCmd_locked()
{
if (cmds.size() > 0) {
int cmd = cmds.front();
cmds.pop();
return cmd;
}
return -1;
}
int getNextCmd(void)
{
int cmd = -1;
std::unique_lock<std::mutex> lk(m);
auto now = std::chrono::system_clock::now();
if (cv.wait_until(lk, now + 100ms, [&cmd,this]{ return ((cmd = maybeCmd_locked()) != -1); }))
{
return cmd;
}
return -1;
}
int sendCmd(int cmd)
{
std::lock_guard<std::mutex> lock(m);
cmds.push(cmd);
cv.notify_one();
return 0;
}
void run(void)
{
int cmd;
printf("run\n");
std::this_thread::sleep_for(std::chrono::milliseconds(10));
while (!threadexit)
{
cmd = getNextCmd();
if (cmd == -1) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else {
printf("cmd received: %d\n", cmd);
}
}
}
};
eventLooper e;
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
std::thread n(&eventLooper::run, &e);
for (int i = 0; i < 10; i++)
{
std::this_thread::sleep_for(1000ms);
e.sendCmd(i);
}
std::this_thread::sleep_for(1000ms);
e.threadexit = true;
n.join();
printf("exit\n");
return 0;
}
The predicate is always checked under the lock, and another wait isn't done if the predicate returns true. In a simplifed version of your code (which doesn't have time outs) is:
if (cv.wait(lk, [&cmd,this]{ return ((cmd = maybeCmd_locked()) != -1); }))
{
return cmd;
}
cv.wait(lock, pred) is defined to be equivalent of:
while(!pred())
{
wait(lock);
}
In this case you can see that your predicate cannot be called twice if it returns true the first time.
Adding the timeout to the question doesn't change how this work. cv.wait_until(...) is the equivalent of:
while (!pred()) {
if (wait_until(lock, timeout_time) == std::cv_status::timeout) {
return pred();
}
}
Again, its clear that what you're worried about cannot happen.
Related
I have the program to count all words in all .log files in given directory using N threads.
I wrote something like this.
ThreadPool.h
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <boost/thread/condition_variable.hpp>
#include <boost/thread.hpp>
#include <future> // I don't how to work with boost future
#include <queue>
#include <vector>
#include <functional>
class ThreadPool
{
public:
using Task = std::function<void()>; // Our task
explicit ThreadPool(int num_threads)
{
start(num_threads);
}
~ThreadPool()
{
stop();
}
template<class T>
auto enqueue(T task)->std::future<decltype(task())>
{
// packaged_task wraps any Callable target
auto wrapper = std::make_shared<std::packaged_task<decltype(task()) ()>>(std::move(task));
{
boost::unique_lock<boost::mutex> lock{ mutex_p };
tasks_p.emplace([=] {
(*wrapper)();
});
}
event_p.notify_one();
return wrapper->get_future();
}
/*void enqueue(Task task)
{
{
boost::unique_lock<boost::mutex> lock { mutex_p };
tasks_p.emplace(std::move(task));
event_p.notify_one();
}
}*/
private:
std::vector<boost::thread> threads_p; // num of threads
std::queue<Task> tasks_p; // Tasks to make
boost::condition_variable event_p;
boost::mutex mutex_p;
bool isStop = false;
void start(int num_threads)
{
for (int i = 0; i < num_threads; ++i)
{
// Add to the end our thread
threads_p.emplace_back([=] {
while (true)
{
// Task to do
Task task;
{
boost::unique_lock<boost::mutex> lock(mutex_p);
event_p.wait(lock, [=] { return isStop || !tasks_p.empty(); });
// If we make all tasks
if (isStop && tasks_p.empty())
break;
// Take new task from queue
task = std::move(tasks_p.front());
tasks_p.pop();
}
// Execute our task
task();
}
});
}
}
void stop() noexcept
{
{
boost::unique_lock<boost::mutex> lock(mutex_p);
isStop = true;
}
event_p.notify_all();
for (auto& thread : threads_p)
{
thread.join();
}
}
};
#endif
main.cpp
#include "ThreadPool.h"
#include <iostream>
#include <iomanip>
#include <Windows.h>
#include <chrono>
#include <vector>
#include <map>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <locale.h>
namespace bfs = boost::filesystem;
//int count_words(boost::filesystem::ifstream& file)
//{
// int counter = 0;
// std::string buffer;
// while (file >> buffer)
// {
// ++counter;
// }
//
// return counter;
//}
//
int count_words(boost::filesystem::path filename)
{
boost::filesystem::ifstream ifs(filename);
return std::distance(std::istream_iterator<std::string>(ifs), std::istream_iterator<std::string>());
}
int main(int argc, const char* argv[])
{
std::cin.tie(0);
std::ios_base::sync_with_stdio(false);
bfs::path path = argv[1];
// If this path is exist and if this is dir
if (bfs::exists(path) && bfs::is_directory(path))
{
// Number of threads. Default = 4
int n = (argc == 3 ? atoi(argv[2]) : 4);
ThreadPool pool(n);
// Container to store all filenames and number of words inside them
//std::map<bfs::path, std::future<int>> all_files_and_sums;
std::vector<std::future<int>> futures;
auto start = std::chrono::high_resolution_clock::now();
// Iterate all files in dir
for (auto& p : bfs::directory_iterator(path)) {
// Takes only .txt files
if (p.path().extension() == ".log") {
// Future for taking value from here
auto fut = pool.enqueue([p]() {
// In this lambda function I count all words in file and return this value
int result = count_words(p.path());
static int count = 0;
++count;
std::ostringstream oss;
oss << count << ". TID, " << GetCurrentThreadId() << "\n";
std::cout << oss.str();
return result;
});
// "filename = words in this .txt file"
futures.emplace_back(std::move(fut));
}
}
int result = 0;
for (auto& f : futures)
{
result += f.get();
}
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::seconds>(stop - start);
std::cout << "Result: " << result << "\n";
std::cout << duration.count() << '\n';
}
else
std::perror("Dir is not exist");
}
Variable N is 4(Number of threads). I've 320 .log files in my directory and I need count words in this files. Everything works fine but when variable "count" is 180 - the program stops for a while and then continues but much slower.
What could be the reason?
CPU - Xeon e5430 (I have tested this program on another CPU - the result is the same).
It depends on how you measure "slow" but basically you are using one of the worst models possible:
one task queue shared between all threads.
The problem with this approach is blocking in each thread on the shared queue.
A much better model is something like
task stealing - you can try creating a task queue pro thread and then use try_lock (which doesnt block) with enabling each thread "stealing" work from some other thread's tasks if it has nothing else to do.
This is very nice explained in excellent Sean Parent Talk about Concurrency.
I wrote the program to count all words in .log files in the different threads and output the result on the screen. First argument in command line is dir to find all .log files and then count words in this files. Second argument in command line is number of threads (by default = 4)
I used the ThreadPool for this program
ThreadPool.h
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <boost/thread/condition_variable.hpp>
#include <boost/thread.hpp>
#include <future> // I don't how to work with boost future
#include <queue>
#include <vector>
#include <functional>
class ThreadPool
{
public:
using Task = std::function<void()>; // Our task
explicit ThreadPool(int num_threads)
{
start(num_threads);
}
~ThreadPool()
{
stop();
}
template<class T>
auto enqueue(T task)->std::future<decltype(task())>
{
// packaged_task wraps any Callable target
auto wrapper = std::make_shared<std::packaged_task<decltype(task()) ()>>(std::move(task));
{
boost::unique_lock<boost::mutex> lock{ mutex_p };
tasks_p.emplace([=] {
(*wrapper)();
});
}
event_p.notify_one();
return wrapper->get_future();
}
//void enqueue(Task task)
//{
// {
// boost::unique_lock<boost::mutex> lock { mutex_p };
// tasks_p.emplace(std::move(task));
// event_p.notify_one();
// }
//}
private:
std::vector<boost::thread> threads_p; // num of threads
std::queue<Task> tasks_p; // Tasks to make
boost::condition_variable event_p;
boost::mutex mutex_p;
bool isStop = false;
void start(int num_threads)
{
for (int i = 0; i < num_threads; ++i)
{
// Add to the end our thread
threads_p.emplace_back([=] {
while (true)
{
// Task to do
Task task;
{
boost::unique_lock<boost::mutex> lock(mutex_p);
event_p.wait(lock, [=] { return isStop || !tasks_p.empty(); });
// If we make all tasks
if (isStop && tasks_p.empty())
break;
// Take new task from queue
task = std::move(tasks_p.front());
tasks_p.pop();
}
// Execute our task
task();
}
});
}
}
void stop() noexcept
{
{
boost::unique_lock<boost::mutex> lock(mutex_p);
isStop = true;
event_p.notify_all();
}
for (auto& thread : threads_p)
{
thread.join();
}
}
};
#endif
main.cpp
#include "ThreadPool.h"
#include <iostream>
#include <iomanip>
#include <Windows.h>
#include <vector>
#include <map>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
namespace bfs = boost::filesystem;
int count_words(const std::string& filename)
{
int counter = 0;
std::ifstream file(filename);
std::string buffer;
while (file >> buffer)
{
++counter;
}
return counter;
}
int main(int argc, const char* argv[])
{
bfs::path path = argv[1];
// If this path is exist and if this is dir
if (bfs::exists(path) && bfs::is_directory(path))
{
// Number of threads. Default = 4
int n = (argc == 3 ? atoi(argv[2]) : 4);
ThreadPool pool(n);
// Container to store all filenames and number of words inside them
std::map<bfs::path, int> all_files_and_sums;
// Iterate all files in dir
for (auto& p : bfs::directory_iterator(path)) {
// Takes only .txt files
if (p.path().extension() == ".log") {
// Future for taking value from here
auto fut = pool.enqueue([&p, &all_files_and_sums]() {
// In this lambda function I count all words in file and return this value
int result = count_words(p.path().string());
std::cout << "TID " << GetCurrentThreadId() << "\n";
return result;
});
// "filename = words in this .txt file"
all_files_and_sums[p.path()] = fut.get();
}
}
int result = 0;
for (auto& k : all_files_and_sums)
{
std::cout << k.first << "- " << k.second << "\n";
result += k.second;
}
std::cout << "Result: " << result << "\n";
}
else
std::perror("Dir is not exist");
}
And this solution works correctly. But if in the directory many files this solution works so slow. I think it's because of the futures. How can I take values from different threads without futures.
(P.S)
Sorry for my english
I have function that returns me a value. I want to use threads for doSth function and set returning value for variables above, here is an example:
#include <string>
#include <iostream>
#include <thread>
using namespace std;
// func for execution
int doSth(int number)
{
return number;
}
int main()
{
// some code ...
int numberOne; // no value now, but in thread I want to set a value from it
int numberTwo; // depending on function input value
thread t1(doSth, 1); // set numberOne = 1;
thread t2(doSth, 2); // set numberTwo = 2;
// wait them to execute
t1.join();
t2.join();
// now I should have numberOne = 1; numberTwo = 2
// some code ...
return 0;
}
How could I do it?
How to return value from std::thread
Besides std::async shown in other answers, you can use std::packaged_task:
std::packaged_task<int(int)> task{doSth};
std::future<int> result = task.get_future();
task(1);
int numberOne = result.get();
This allows separating creation of the task, and executing it in case that is needed.
Method 1: Using std::async (higher-level wrapper for threads and futures):
#include <thread>
#include <future>
#include <iostream>
int func() { return 1; }
int main(){
std::future<int> ret = std::async(&func);
int i = ret.get();
std::cout<<"I: "<<i<<std::endl;
return 0;
}
Method 2: Using threads and futures:
#include <thread>
#include <future>
#include <iostream>
void func(std::promise<int> && p) {
p.set_value(1);
}
int main(){
std::promise<int> p;
auto f = p.get_future();
std::thread t(&func, std::move(p));
t.join();
int i = f.get();
std::cout<<"I: "<<i<<std::endl;
return 0;
}
My prefered method is encapsulate the call in a specific method returning nothing (and managing error in the same way).
void try_doSth(int number, int* return_value, int* status)
{
try
{
*return_value = doSth(number);
*status = 0;
}
catch(const std::exception& e) { *status = 1; }
catch(...) { *status = 2; }
}
int r1,r2,s1,s2;
std::thread t1(try_doSth, 1, &r1, &s1);
std::thread t2(try_doSth, 2, &r2, &s2);
i am trying to find a solution to start and stop multiple threads in a infinite loops.
Each thread should be a seperated task and should run parallel. The threads are starting in a infinite loop and the thread itself got also in a infinite loop. Each loop should stop with the "GetKeyState" so i should be able to toggle the threads. But i am not able to start e.g. 2 Threads (Functions of the program), because the .join() is blocking the execution, and without the .join() the threading does not work.
Do you guys have a possible solution for this problem? The toggle start of one Thread is equal to the toggle stop of the infinite loop of the thread.
Here is some code i tried
#include <iostream>
#include <thread>
#include <Windows.h>
class KeyToggle {
public:
KeyToggle(int key) :mKey(key), mActive(false) {}
operator bool() {
if (GetAsyncKeyState(mKey)) {
if (!mActive) {
mActive = true;
return true;
}
}
else
mActive = false;
return false;
}
private:
int mKey;
bool mActive;
};
KeyToggle toggle(VK_NUMPAD1);
KeyToggle toggle2(VK_NUMPAD2);
void threadFunc() {
while (!toggle) {
std::cout << "Thread_1\n";
}
}
void threadFunc2() {
while (!toggle2) {
std::cout << "Thread_2\n";
}
}
int main()
{
bool bToggle = false;
bool bToggle2 = false;
std::thread t1;
std::thread t2;
while (!GetKeyState(VK_NUMPAD0)) {
if (toggle) {
bToggle = !bToggle;
if (bToggle) {
std::thread t1(threadFunc);
t1.join();
}
}
if (toggle2) {
bToggle2 = !bToggle2;
if (bToggle2) {
std::thread t2(threadFunc2);
t2.join();
}
}
}
}
Solution with the idea of #Someprogrammerdude
#include <iostream>
#include <thread>
#include <Windows.h>
#include <atomic>
std::atomic<bool> aToggle1 = false;
std::atomic<bool> aToggle2 = false;
std::atomic<bool> aToggleStopAll = false;
class KeyToggle {
public:
KeyToggle(int key) :mKey(key), mActive(false) {}
operator bool() {
if (GetAsyncKeyState(mKey)) {
if (!mActive) {
mActive = true;
return true;
}
}
else
mActive = false;
return false;
}
private:
int mKey;
bool mActive;
};
KeyToggle toggle(VK_NUMPAD1);
KeyToggle toggle2(VK_NUMPAD2);
void threadFunc() {
while(aToggleStopAll==false)
{
if(aToggle1) { std::cout << "Thread_1\n"; }
}
}
void threadFunc2() {
while(aToggleStopAll==false)
{
if(aToggle2) { std::cout << "Thread_2\n"; }
}
}
int main()
{
std::thread t1(threadFunc);
std::thread t2(threadFunc2);
while (!GetKeyState(VK_NUMPAD0)) {
if (toggle) {
aToggle1 = !aToggle1;
}
if (toggle2) {
aToggle2 = !aToggle2;
}
}
aToggleStopAll = true;
t1.join();
t2.join();
}
I am using an online C++11 compiler, link found here: cpp.sh (C++ Shell).
In my current project, I would like to have a watchdog class, to be able to check somehow the status of a thread or FSM (for example).
After some work (I'm not a C++11 guru), I finally got the code below, that compiles ok.
I also did some basic/trivial tests, but it seems the test program doesn't want to exit.
It says "Program running" and the only way to (force) exit is to hit the "Stop" button... :(
Well, my question : What am I doing wrong?
Any ideas, suggestions you can provide are highly appreciated.
Here is the full code, including my test app:
Watchdog (as MCVE):
#include <thread>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <iostream>
using namespace std::chrono;
class Watchdog
{
public:
Watchdog();
~Watchdog();
void Start(unsigned int milliseconds, std::function<void()> callback = 0);
void Stop();
void Pet();
private:
unsigned int m_interval;
std::atomic<bool> m_running;
std::thread m_thread;
std::function<void()> m_callback;
std::mutex m_mutex;
steady_clock::time_point m_lastPetTime;
std::condition_variable m_stopCondition;
void Loop();
};
Watchdog::Watchdog()
{
m_running = false;
}
Watchdog::~Watchdog()
{
Stop();
}
void Watchdog::Start(unsigned int milliseconds, std::function<void()> callback)
{
std::unique_lock<std::mutex> locker(m_mutex);
if(m_running == false)
{
m_lastPetTime = steady_clock::now();
m_interval = milliseconds;
m_callback = callback;
m_running = true;
m_thread = std::thread(&Watchdog::Loop, this);
}
}
void Watchdog::Stop()
{
std::unique_lock<std::mutex> locker(m_mutex);
if(m_running == true)
{
m_running = false;
m_stopCondition.notify_all();
m_thread.join();
}
}
void Watchdog::Pet()
{
std::unique_lock<std::mutex> locker(m_mutex);
m_lastPetTime = steady_clock::now();
m_stopCondition.notify_all();
}
void Watchdog::Loop()
{
std::unique_lock<std::mutex> locker(m_mutex);
while(m_running == true)
{
if(m_stopCondition.wait_for(locker, milliseconds(m_interval)) == std::cv_status::timeout)
{
if(m_callback != nullptr)
m_callback();
}
}
}
int main(int argc, char *argv[])
{
Watchdog wdog;
wdog.Start(3000, [] { std::cout << " WDOG TRIGGERED!!! "; });
for(auto i = 0; i < 10; i++)
{
std::cout << "[+]";
wdog.Pet();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
-
You're doing a deadlock here.
void Watchdog::Stop()
{
std::unique_lock<std::mutex> locker(m_mutex);
if(m_running == true)
{
m_running = false;
m_stopCondition.notify_all();
m_thread.join();
^ ~~~~~~~~~~~~~~
m_mutex is locked; m_thread cannot continue execution
}
}
Some additional suggestion: use simple if conditions, do not compare with true or false.