I'm facing a problem in programmation where I did not find any convenient and fast execution solutions.
I'm trying to implement some kind of state machine : take one byte in entry, process it, change state, loop, etc... The purpose is to process byte stream without requiring any memory buffer (processing byte per byte).
The class should looks like this :
class Decoder {
void next() {
int i = 0;
std::cout << i << "\n";
i++;
yield(); // pseudo code => should stop the function and save the current state (or simply not freeing allocated variables)
std::cout << i << "\n";
}
};
Decoder decoder = Decoder();
decoder.next(); // print 1
std::cout << "1.5" << "\n"; // print 1.5
decoder.next(); // print 2
A solution could be to create a step property to save the step and then resume with a switch but the performances would be strongly impacted. I would like to know if there is a way to exit the execution of a function and then resume it later ?
To be clear, I don't want to pause the entire program, only a function. Pausing such a function would return to the caller and continue the execution of the program until the next next is called.
Moreover, I would like to avoid thread and std as much (I prefer all environnements code). Finally, if you have any other alternatives to my problem : process byte stream efficiently for the memory, I'm open to your suggestions.
Thanks for your help.
I believe you could achieve just that using these two ways:
Option 1: Member state
Split the state machine object into a separate object, and convert all your local variables to members.
For each step of the way, save a State member, signifying where are you now throughout the execution of your program.
Each time you enter next() check your state against a switch and call the designated inner method for that step.
Each such step method simulates code execution between to consecutive yields.
struct Decoder {
void next() {
switch (_step) {
case s1:
step1();
_step = s2;
return;
case s2:
step2();
_step = s1;
return;
default:
return; // handle error...
}
}
private:
enum Step { s1, s2 };
Step _step = s1;
int _i = 1;
void step1() {
std::cout << _i << "\n";
_i++;
}
void step2() {
std::cout << _i << "\n";
}
};
int main() {
Decoder decoder = Decoder();
decoder.next(); // print 1
std::cout << "1.5" << "\n"; // print 1.5
decoder.next(); // print 2
}
Option 2: Thread and signalling
Use a thread, which you could of course run using native APIs (e.g. pthread_create on POSIX platforms).
Inside your thread, every time you want to yield, wait on a conditional variable, e.g:
struct Decoder {
Decoder() {
_thread = std::thread { &Decoder::worker, this };
}
~Decoder() {
_thread.join();
}
void next() {
std::lock_guard<std::mutex> lock(_mutex);
_work = true;
}
private:
void wait() {
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [this](){return _work;});
}
void worker() {
wait();
int i = 0;
std::cout << i << "\n";
i++;
wait();
std::cout << i << "\n";
}
std::thread _thread;
std::mutex _mutex;
std::condition_variable _cond;
bool _work = false;
};
int main() {
Decoder decoder;
decoder.next(); // print 1
std::cout << "1.5" << "\n"; // print 1.5
decoder.next(); // print 2
}
Related
I'm studying concurrency in C++ and I'm trying to implement a multithreaded callback registration system. I came up with the following code, which is supposed to accept registration requests until an event occurs. After that, it should execute all the registered callbacks in order with which they were registered. The registration order doesn't have to be deterministic.
The code doesn't work as expected. First of all, it rarely prints the "Pushing callback with id" message. Secondly, it sometimes hangs (a deadlock caused by a race condition, I assume). I'd appreciate help in figuring out what's going on here. If you see that I overcomplicate some parts of the code or misuse some pieces, please also point it out.
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
class CallbackRegistrar{
public:
void registerCallbackAndExecute(std::function<void()> callback) {
if (!eventTriggered) {
std::unique_lock<std::mutex> lock(callbackMutex);
auto saved_id = callback_id;
std::cout << "Pushing callback with id " << saved_id << std::endl;
registeredCallbacks.push(std::make_pair(callback_id, callback));
++callback_id;
callbackCond.wait(lock, [this, saved_id]{return releasedCallback.first == saved_id;});
releasedCallback.second();
callbackExecuted = true;
eventCond.notify_one();
}
else {
callback();
}
}
void registerEvent() {
eventTriggered = true;
while (!registeredCallbacks.empty()) {
releasedCallback = registeredCallbacks.front();
callbackCond.notify_all();
std::unique_lock<std::mutex> lock(eventMutex);
eventCond.wait(lock, [this]{return callbackExecuted;});
callbackExecuted = false;
registeredCallbacks.pop();
}
}
private:
std::queue<std::pair<unsigned, std::function<void()>>> registeredCallbacks;
bool eventTriggered{false};
bool callbackExecuted{false};
std::mutex callbackMutex;
std::mutex eventMutex;
std::condition_variable callbackCond;
std::condition_variable eventCond;
unsigned callback_id{1};
std::pair<unsigned, std::function<void()>> releasedCallback;
};
int main()
{
CallbackRegistrar registrar;
std::thread t1(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "First!\n";});
std::thread t2(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "Second!\n";});
registrar.registerEvent();
t1.join();
t2.join();
return 0;
}
This answer has been edited in response to more information being provided by the OP in a comment, the edit is at the bottom of the answer.
Along with the excellent suggestions in the comments, the main problem that I have found in your code is with the callbackCond condition variable wait condition that you have set up. What happens if releasedCallback.first does not equal savedId?
When I have run your code (with a thread-safe queue and eventTriggered as an atomic) I found that the problem was in this wait function, if you put a print statement in that function you will find that you get something like this:
releasedCallback.first: 0, savedId: 1
This then waits forever.
In fact, I've found that the condition variables used in your code aren't actually needed. You only need one, and it can live inside the thread-safe queue that you are going to build after some searching ;)
After you have the thread-safe queue, the code from above can be reduced to:
class CallbackRegistrar{
public:
using NumberedCallback = std::pair<unsigned int, std::function<void()>>;
void postCallback(std::function<void()> callback) {
if (!eventTriggered)
{
std::unique_lock<std::mutex> lock(mutex);
auto saved_id = callback_id;
std::cout << "Pushing callback with id " << saved_id << std::endl;
registeredCallbacks.push(std::make_pair(callback_id, callback));
++callback_id;
}
else
{
while (!registeredCallbacks.empty())
{
NumberedCallback releasedCallback;
registeredCallbacks.waitAndPop(releasedCallback);
releasedCallback.second();
}
callback();
}
}
void registerEvent() {
eventTriggered = true;
}
private:
ThreadSafeQueue<NumberedCallback> registeredCallbacks;
std::atomic<bool> eventTriggered{false};
std::mutex mutex;
unsigned int callback_id{1};
};
int main()
{
CallbackRegistrar registrar;
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++)
{
threads.push_back(std::thread(&CallbackRegistrar::postCallback,
std::ref(registrar),
[i]{std::cout << std::to_string(i) <<"\n";}
));
}
registrar.registerEvent();
for (auto& thread : threads)
{
thread.join();
}
return 0;
}
I'm not sure if this does exactly what you want, but it doesn't deadlock. It's a good starting point in any case, but you need to bring your own implementation of ThreadSafeQueue.
Edit
This edit is in response to the comment by the OP stating that "once the event occurs, all the callbacks should be executed in [the] order that they've been pushed to the queue and by the same thread that registered them".
This was not mentioned in the original question post. However, if that is the required behaviour then we need to have a condition variable wait in the postCallback method. I think this is also the reason why the OP had the condition variable in the postCallback method in the first place.
In the code below I have made a few edits to the callbacks, they now take input parameters. I did this to print some useful information while the code is running so that it is easier to see how it works, and, importantly how the condition variable wait is working.
The basic idea is similar to what you had done, I've just trimmed out the stuff you didn't need.
class CallbackRegistrar{
public:
using NumberedCallback = std::pair<unsigned int, std::function<void(int, int)>>;
void postCallback(std::function<void(int, int)> callback, int threadId) {
if (!m_eventTriggered)
{
// Lock the m_mutex
std::unique_lock<std::mutex> lock(m_mutex);
// Save the current callback ID and push the callback to the queue
auto savedId = m_currentCallbackId++;
std::cout << "Pushing callback with ID " << savedId << "\n";
m_registeredCallbacks.push(std::make_pair(savedId, callback));
// Wait until our thread's callback is next in the queue,
// this will occur when the ID of the last called callback is one less than our saved callback.
m_conditionVariable.wait(lock, [this, savedId, threadId] () -> bool
{
std::cout << "Waiting on thread " << threadId << " last: " << m_lastCalledCallbackId << ", saved - 1: " << (savedId - 1) << "\n";
return (m_lastCalledCallbackId == (savedId - 1));
});
// Once we are finished waiting, get the callback out of the queue
NumberedCallback retrievedCallback;
m_registeredCallbacks.waitAndPop(retrievedCallback);
// Update last callback ID and call the callback
m_lastCalledCallbackId = retrievedCallback.first;
retrievedCallback.second(m_lastCalledCallbackId, threadId);
// Notify one waiting thread
m_conditionVariable.notify_one();
}
else
{
// If the event is already triggered, call the callback straight away
callback(-1, threadId);
}
}
void registerEvent() {
// This is all we have to do here.
m_eventTriggered = true;
}
private:
ThreadSafeQueue<NumberedCallback> m_registeredCallbacks;
std::atomic<bool> m_eventTriggered{ false};
std::mutex m_mutex;
std::condition_variable m_conditionVariable;
unsigned int m_currentCallbackId{ 1};
std::atomic<unsigned int> m_lastCalledCallbackId{ 0};
};
The main function is as above, except I am creating 100 threads instead of 10, and I have made the callback print out information about how it was called.
for (int createdThreadId = 0; createdThreadId < 100; createdThreadId++)
{
threads.push_back(std::thread(&CallbackRegistrar::postCallback,
std::ref(registrar),
[createdThreadId](int registeredCallbackId, int callingThreadId)
{
if (registeredCallbackId < 0)
{
std::cout << "Callback " << createdThreadId;
std::cout << " called immediately, from thread: " << callingThreadId << "\n";
}
else
{
std::cout << "Callback " << createdThreadId;
std::cout << " called from thread " << callingThreadId;
std::cout << " after being registered as " << registeredCallbackId << "\n";
}
},
createdThreadId));
}
I am not entirely sure why you want to do this, as it seems to defeat the point of having multiple threads, although I may be missing something there. But, regardless, I hope this helps you to understand better the problem you are trying to solve.
Experimenting with this code some more, I found out why the "Pushing callback with id " part was rarely printed. It's because the call to registrar.registerEvent from the main thread was usually faster than the calls to registerCallbackAndExecute from separate threads. Because of that, the condition if (!eventTriggered) was almost never fulfilled (eventTriggered had been set to true in the registerEvent method) and hence all calls to registerCallbackAndExecute were falling into the else branch and executing straightaway.
Then, the program sometimes also didn't finish, because of a race condition between registerEvent and registerCallbackAndExecute. Sometimes, registerEvent was being called after the check if (!eventTriggered) but before pushing the callback to the queue. Then, registerEvent completed instantly (as the queue was empty) while the thread calling registerCallbackAndExecute was pushing the callback to the queue. The latter thread then kept waiting forever for the event (that had already happened) to happen.
I am trying to create a sort of threadpool that runs functions on separate threads and only starts a new iteration when all functions have finished.
map<size_t, bool> status_map;
vector<thread> threads;
condition_variable cond;
bool are_all_ready() {
mutex m;
unique_lock<mutex> lock(m);
for (const auto& [_, status] : status_map) {
if (!status) {
return false;
}
}
return true;
}
void do_little_work(size_t id) {
this_thread::sleep_for(chrono::seconds(1));
cout << id << " did little work..." << endl;
}
void do_some_work(size_t id) {
this_thread::sleep_for(chrono::seconds(2));
cout << id << " did some work..." << endl;
}
void do_much_work(size_t id) {
this_thread::sleep_for(chrono::seconds(4));
cout << id << " did much work..." << endl;
}
void run(const function<void(size_t)>& function, size_t id) {
while (true) {
mutex m;
unique_lock<mutex> lock(m);
cond.wait(lock, are_all_ready);
status_map[id] = false;
cond.notify_all();
function(id);
status_map[id] = true;
cond.notify_all();
}
}
int main() {
threads.push_back(thread(run, do_little_work, 0));
threads.push_back(thread(run, do_some_work, 1));
threads.push_back(thread(run, do_much_work, 2));
for (auto& thread : threads) {
thread.join();
}
return EXIT_SUCCESS;
}
I expect to get the output:
0 did little work...
1 did some work...
2 did much work...
0 did little work...
1 did some work...
2 did much work...
.
.
.
after the respective timeouts but when I run the program I only get
0 did little work...
0 did little work...
.
.
.
I also have to say that Im rather new to multithreading but in my understanding, the condition_variable should to the taks of blocking every thread till the predicate returns true. And in my case are_all_ready should return true after all functions have returned.
There are several ways to do this.
Easiest in my opinion would be a C++20 std::barrier, which says, "wait until all of N threads have arrived and are waiting here."
#include <barrier>
std::barrier synch_workers(3);
....
void run(const std::function<void(size_t)>& func, size_t id) {
while (true) {
synch_workers.arrive_and_wait(); // wait for all three to be ready
func(id);
}
}
Cruder and less efficient, but equally effective, would be to construct and join() new sets of three worker threads for each "batch" of work:
int main(...) {
std::vector<thread> threads;
...
while (flag_running) {
threads.push_back(...);
threads.push_back(...);
...
for (auto& thread : threads) {
thread.join();
}
threads.clear();
}
Aside
I'd suggest you revisit some core synchronization concepts, however. You are using new mutexes when you want to re-use a shared one. The scope of your unique_lock isn't quite right.
Now, your idea to track worker thread "busy/idle" state in a map is straightforward, but cannot correctly coordinate "batches" or "rounds" of work that must be begun at the same time.
If a worker sees in the map that two of three threads, including itself, are "idle", what does that mean? Is a "batch" of work concluding — i.e., two workers are waiting for a tardy third? Or has a batch just begun — i.e., the two idle threads are tardy and had better get to work like their more eager peer?
The threads cannot know the answer without keeping track of the current batch of work, which is what a barrier (or its more complex cousin the phaser) does under the hood.
As-is, your program has a crash (UB) due to concurrent access to status_map.
When you do:
void run(const function<void(size_t)>& function, size_t id)
{
...
mutex m;
unique_lock<mutex> lock(m);
...
status_map[id] = false;
the locks created are local variables, one per thread, and as such independent. So, it doesn't prevent multiple threads from writing to status_map at once, and thus crashing. That's what I get on my machine.
Now, if you make the mutex static, only one thread can access the map at once. But that also makes it so that only one thread runs at once. With this I see 0, 1 and 2 running, but only once at a time and a strong tendency for the previous thread to have run to run again.
My suggestion, go back to the drawing board and make it simpler. All threads run at once, single mutex to protect the map, only lock the mutex to access the map, and ... well, in fact, I don't even see the need for a condition variable.
e.g. what is wrong with:
#include <thread>
#include <iostream>
#include <vector>
using namespace std;
vector<thread> threads;
void do_little_work(size_t id) {
this_thread::sleep_for(chrono::seconds(1));
cout << id << " did little work..." << endl;
}
void do_some_work(size_t id) {
this_thread::sleep_for(chrono::seconds(2));
cout << id << " did some work..." << endl;
}
void do_much_work(size_t id) {
this_thread::sleep_for(chrono::seconds(4));
cout << id << " did much work..." << endl;
}
void run(const function<void(size_t)>& function, size_t id) {
while (true) {
function(id);
}
}
int main() {
threads.push_back(thread(run, do_little_work, 0));
threads.push_back(thread(run, do_some_work, 1));
threads.push_back(thread(run, do_much_work, 2));
for (auto& thread : threads) {
thread.join();
}
return EXIT_SUCCESS;
}
I have a thread that is doing "work", it is supposed to report progress when conditional variable notifies it. This thread is waiting for conditional variables.
Other thread is waiting for a x amount of milliseconds and then notifies conditional variable to proceed.
I have 5 conditional variables (this is an exercise for school) and once each gets notified work progress is supposed to be reported:
Problem im having is that thread 2, the one that is supposed to notify thread 1, goes through all 5 checkPoints and notifies only once in the end. So I end up in a situation where progress is at 20% in the end and thread 1 is waiting for another notify but thread 2 has finished all notifies.
Where is flaw in my implementation of this logic?
Code below:
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Program {
public:
Program() {
m_progress = 0;
m_check = false;
}
bool isWorkReady() { return m_check; }
void loopWork() {
cout << "Working ... : " << endl;
work(m_cv1);
work(m_cv2);
work(m_cv3);
work(m_cv4);
work(m_cv5);
cout << "\nFinished!" << endl;
}
void work(condition_variable &cv) {
unique_lock<mutex> mlock(m_mutex);
cv.wait(mlock, bind(&Program::isWorkReady, this));
m_progress++;
cout << " ... " << m_progress * 20 << "%" << endl;
m_check = false;
}
void checkPoint(condition_variable &cv) {
lock_guard<mutex> guard(m_mutex);
cout << " < Checking >" << m_progress << endl;
this_thread::sleep_for(chrono::milliseconds(300));
m_check = true;
cv.notify_one();
}
void loopCheckPoints() {
checkPoint(m_cv1);
checkPoint(m_cv2);
checkPoint(m_cv3);
checkPoint(m_cv4);
checkPoint(m_cv5);
}
private:
mutex m_mutex;
condition_variable m_cv1, m_cv2, m_cv3, m_cv4, m_cv5;
int m_progress;
bool m_check;
};
int main() {
Program program;
thread t1(&Program::loopWork, &program);
thread t2(&Program::loopCheckPoints, &program);
t1.join();
t2.join();
return 0;
}
The loopCheckPoints() thread holds a lock for some time, sets m_check then releases the lock and immediately goes on to grab the lock again. The loopWork() thread may not have woken up in between to react to the m_check change.
Never hold locks for long times. Be as quick as possible. If you can't get the program to work without adding sleeps, you have a problem.
One way to fix this would be to check that the worker has actually set m_check back to false:
void work(condition_variable& cv) {
{ // lock scope
unique_lock<mutex> mlock(m_mutex);
cv.wait(mlock, [this] { return m_check; });
m_progress++;
cout << " ... " << m_progress * 20 << "%" << endl;
m_check = false;
}
// there's no need to hold the lock when notifying
cv.notify_one(); // notify that we set it back to false
}
void checkPoint(condition_variable& cv) {
// if you are going to sleep, do it without holding the lock
// this_thread::sleep_for(chrono::milliseconds(300));
{ // lock scope
lock_guard<mutex> guard(m_mutex);
cout << "<Checking> " << m_progress << endl;
m_check = true;
}
cv.notify_one(); // no need to hold the lock here
{
// Check that m_check is set back to false
unique_lock<mutex> mlock(m_mutex);
cv.wait(mlock, [this] { return not m_check; });
}
}
Where is flaw in my implementation of this logic?
cv.notify_one does not require, that the code after cv.wait(mlock, bind(&Program::isWorkReady, this)); continues immediatly, so it is perfectly valid that multiple checkPoint are exectued, before the code continues after cv.wait.
But after you the cv.wait you set m_check = false; to false, so if there is no further checkPoint execution remaining, that will set m_check = true;, your work function becomes stuck.
Instead of m_check being a bool you could think about making it a counter, that is incremented in checkPoint and decremented in work.
I have written a basic server in C++ that runs in an infinite while loop. It receives signals from a client to do things. The main process that I want is to initiate or stop some tracking software that I have written.
I would like the server to still be able to receive signals while the tracking software is being run (e.g. if a stop signal was given). I figured that the best way to do this would be to create a separate thread for the tracking software, so that is what I did:
void Server::tracking(Command c)
{
//I have since changed this method. The new implementation is below
//switch(c) {
// case START:
// player = VideoPlayer();
// player.setTrackStatus(true);
// t = std::thread(&Server::track, this);
// t.detach();
// break;
// case STOP:
// player.setTrackStatus(false);
// break;
// default:
// break;
//}
}
Server::track just calls player.run()
VideoPlayer is the class that contains the main tracking loop. The track status is what determines whether or not the tracking loop continues to execute.
This works fine the first time I run it, it is able to start the tracking and stop it. The problem arises when I try to send another "START" signal without restarting the server.
I have narrowed down the problem to the cv::namedWindow function.
Here is the start of the VideoPlayer class:
void VideoPlayer::run(void)
{
//I have since changed this method. The new implementation is below
//initVC();
//openStream();
}
initVC() is where I create the namedWindow and openStream contains the main tracking loop. Here is initVC (which is where I believe the problem lies):
void VideoPlayer::initVC()
{
if(!capture.open("cut.mp4")) {
throw "Cannot open video stream";
}
std::cout << "flag 1" << std::endl;
cv::namedWindow("Tracker", CV_WINDOW_AUTOSIZE);
std::cout << "flag 2" << std::endl;
}
I have found that on the second run (i.e. tracking has been started and stopped and the server has not been closed and reopened), that flag 2 never gets run. I also found that, if I omit namedWindow then the program stops before imshow(). It might also be worth noting that the program doesn't crash, it just seems to pause.
I have a feeling that I am doing something wrong with the threading, because I have never used threads in C++ before.
Thanks!
EDIT: I have been attempting to add some of the changes suggested by #Dom, however I am still having a similar issue to before. I will post some additional code below with comments to try to explain.
Server::tracking:
This is meant to initiate tracking based on the command received from the client.
void Server::tracking(Command c)
{
switch(c) {
case START:
if(!isRunning) {
player = make_unique<VideoPlayer>();
isRunning = true;
player->setTrackStatus(isRunning);
}
else {
std::lock_guard<std::mutex> lock(mtx);
}
break;
case STOP:
if(isRunning) {
player->terminate();
player->exit(); //Destroys OpenCV stuff
player->joinThread();
player = nullptr;
isRunning = false;
}
else {
std::lock_guard<std::mutex> lock(mtx);
}
break;
default:
break;
}
}
VideoPlayer Constructor:
VideoPlayer::VideoPlayer () : trackStatus(true)
{
tracker = Tracker(); //A separate class, related to the data from the tracked
//object. Not relevant to the current question
track_t = std::thread(&VideoPlayer::run, this);
return;
}
VideoPlayer::run:
void VideoPlayer::run(void)
{
std::lock_guard<std::mutex> lock(mtx);
initVC(); //Initialises the OpenCV VideoCapture
openStream(); //Contains the main tracking code
return;
}
VideoPlayer::openStream:
void VideoPlayer::openStream()
{
while(trackStatus) {
... //tracking stuff
}
return;
}
VideoPlayer::terminate:
void VideoPlayer::terminate()
{
track = false;
std::lock_guard<std::mutex> lock(mtx);
}
VideoPlayer::joinThread:
void VideoPlayer::joinThread()
{
if(track_t.joinable()) {
std::cout << "flag 1" << std::endl;
track_t.join();
std::cout << "flag 2" << std::endl; //It fails here on my second "run"
return;
}
}
Basically, my program stops just before the track_t.join(), the second time I run the tracking (without restarting the server). flag 1 and flag 2 print the first time that I run the tracking. All of the OpenCV components appear to have been disposed of correctly. If I then try to open the tracking again, firstly, the tracking doesn't seem to start (but the program doesn't crash), and then if I try to stop the tracking, it prints flag 1 but then stops indefinitely without printing flag 2
Sorry for the lengthy post. I hope this gives a bit more context to what I'm trying to achieve
So your tracking app. could be implemented as follows:
#include <chrono>
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <memory>
#include <atomic>
enum Command : char
{
START = '1',
STOP = '0'
};
static std::mutex mtx; // mutex for I/O stream
class VideoPlayer
{
public:
VideoPlayer() : trackStatus()
{
initVC();
openStream();
};
~VideoPlayer()
{
closeStream();
uninitVC();
}
void setTrackStatus(bool status)
{
if (status && trackStatus == false)
{
trackStatus = status;
t = std::thread(&VideoPlayer::run, this);
}
else
{
trackStatus = false;
if (t.joinable())
{
t.join();
}
}
}
private:
void run()
{
tId = std::this_thread::get_id();
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "run thread: " << tId << std::endl;
}
while (trackStatus)
{
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "...running thread: " << tId << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(1)); // encode chunk of stream and play, whatever....
}
}
void initVC()
{
/*
if (!capture.open("cut.mp4"))
{
throw "Cannot open video stream"; --> http://stackoverflow.com/questions/233127/how-can-i-propagate-exceptions-between-threads
}
std::cout << "flag 1" << std::endl;
//cv::namedWindow("Tracker", CV_WINDOW_AUTOSIZE);
//std::cout << "flag 2" << std::endl;
*/
}
void uninitVC()
{
}
void openStream()
{
}
void closeStream()
{
}
private:
std::atomic<bool> trackStatus; // atomic, because of access from another (main) thread
std::thread t; // thread for "tracking"
std::thread::id tId; // ID of the "tracking" thread
};
class Server
{
public:
Server() : isRunning(), player(std::make_unique<VideoPlayer>())
{
}
~Server() = default;
void tracking(Command c)
{
switch (c)
{
case START:
if (!isRunning)
{
isRunning = true;
player->setTrackStatus(isRunning);
}
else
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Player is already running...\n";
}
break;
case STOP:
if (isRunning)
{
player->setTrackStatus(!isRunning);
isRunning = false;
}
else
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Player is not running...\n";
}
break;
default:
break;
}
}
private:
std::unique_ptr<VideoPlayer> player;
bool isRunning;
};
int main()
{
std::cout << "main thread: " << std::this_thread::get_id() << std::endl;
Server srv;
char cmd = -1;
while (std::cin >> cmd)
{
switch (cmd)
{
case Command::START:
{
srv.tracking(Command::START);
}
break;
case Command::STOP:
{
srv.tracking(Command::STOP);
}
break;
default:
std::cout << "Unknown command...\n";
break;
}
}
}
You can move creation of the thread to constructor of VideoPlayer and join in destructor (I would prefer it...):
VideoPlayer() : trackStatus(true)
{
initVC();
openStream();
t = std::thread(&VideoPlayer::run, this);
};
~VideoPlayer()
{
closeStream();
uninitVC();
if (t.joinable())
{
t.join();
}
}
but some modifications are needed to terminate and clean the thread, you can use something like
public:
void VideoPlayer::terminate()
{
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "terminate thread: " << tId << std::endl;
}
trackStatus = false;
}
however, than is needed to create instance of player during START
player = std::make_unique<VideoPlayer>();
and then Terminate() and delete the player during STOP
player->terminate();
player = nullptr;
Hope, this inspired you enough ;-)
I would like to read in parallel line by line from output file. Every thread read one line then work with data. In the mean time next thread have to read the next line.
std::ifstream infile("test.txt");
std::mutex mtx;
void read(int id_thread){
while(infile.good()){
mtx.lock();
std::string sLine;
getline(infile, sLine);
std::cout << "Read by thread: " << id_thread;
std::cout << sLine << std::endl;
mtx.unlock();
}
}
void main(){
std::vector<std::thread> threads;
for(int i = 0; i < num; i++){
threads.push_back(std::thread(parallelFun, i));
}
for(auto& thread : threads){
thread.join();
}
return 0;
}
When i run this code i get this:
First thread read all lines. How can i make it happen that every thread read one line?
EDIT
As mentioned in the comments all i needed to do was bigger test file.
Thanks guys!
I would change the loop into
while(infile.good()){
mtx.lock();
std::string sLine;
getline(infile, sLine);
mtx.unlock();
std::cout << "Read by thread: " << id_thread;
std::cout << sLine << std::endl;
}
Your std::cout stuff is the busy part of your test loop which you want to exchange for real code later. This gives the other thread time to kick in. Furthermore make your test file large. It is not uncommon that thread initialization takes some time in which the first thread eats all the data.
If you want your 5 threads to read exactly every 5th line you must synchronize the reads, so each thread must know that the previous has finished reading its part. This requirement potentially impose a huge inefficiency as some threads could be waiting a long time for the previous in order to run.
Concept code, untested use at own risk.
Lets first make a default class to handle atomic locks. We align it to avoid false sharing and the associated cache ping-pong.
constexpr size_t CACHELINESIZE = 64; // could differ on your architecture
template<class dType>
class alignas(CACHELINESIZE) lockstep {
std::atomic<dType> lock = dType(0);
public:
// spinlock spins until the previous value is prev and then tries to set lock to value
// until success, restart the spin if prev changes.
dType Spinlock(dType prev = dType(0), dType next = dType(1)) {
dType expected = prev;
while (!lock.compare_exchange_weak(expected, next)) { // request for locked-exclusiv ~100 cycles?
expected = prev; // we wish to continue to wait for expected
do {
pause(); // on intel waits roughly one L2 latency time.
} while(lock.load(std::memory_order_relaxed) != prev); // only one cache miss per change
}
return expected;
}
void store(dType value) {
lock.store(value);
}
};
lockstep<int> lock { 0 };
constexpr int NoThreads = 5;
std::ifstream infile("test.txt");
void read(int id_thread) {
locks[id_thread].lock = id_thread;
bool izNoGood = false;
int next = id_thread;
while(!izNoGood){
// get lock for next iteration
lock.spinlock(next, next); // wait on our number
// moved file check into locked region
izNoGood = !infile.good();
if (izNoGood) {
lock.store(next+1); // release next thread to end run.
return;
}
std::string sLine;
getline(infile, sLine);
// release next thread
lock.store(next+1);
// do work asynchronous
// ...
// debug log, hopefully the whole line gets written in one go (atomic)
// but can be in "random" order relative to other lines.
std::cout << "Read by thread: " << id_thread << " line no. " << next
<< " text:" << sLine << std::endl; // endl flushes cout, implicit sync?
next += NoThreads; // our next expected line to process
}
}
void main() {
std::vector<std::thread> threads;
for(int i = 0; i < NoThreads; i++) {
threads.push_back(std::thread(parallelFun, i));
}
for(auto& thread : threads){
thread.join();
}
return 0;
}
Just in case you want each thread to read a single line ( which is obvious from your description ) , remove the while loop and then you need to make sure that you have same number of threads as number of lines in file.
To get rid of above constraint you can use boost threadpool.