Reputation: 774
Program language: C++ 11
I use pipeline threads mode to deal data.
One thread generate data.
One thread process data.
While no data to deal, which is the best way to yield thread?
Now I use
std::this_thread::sleep_for(100ms);
Upvotes: 1
Views: 2356
Reputation: 275310
Communicate over a queue of data. The queue can have a max size if you want.
One thread generates data and shoves it into the queue.
Another thread pops data off the queue, and processes it.
When the queue is empty, the consuming thread waits in the queue for new data to be pushed.
When the queue is full, the producing thread waits in in the queue for data to be popped. Alternatively, you could discard and replace waiting data in the queue. Which depends on what your data model is like (most recent is important, or everything in order is important)
Simple queue:
template<class T>
struct threadsafe_queue {
T pop() {
auto l = lock();
cv.wait( l, [&]{ return !data.empty(); } );
T r = std::move(data.front());
data.pop_front();
return r;
}
void push( T&& t ) {
auto l = lock();
data.push_back( std::move(t) );
cv.notify_one();
}
void abandon() {
auto l = lock();
data = {};
}
private:
std::unique_lock<std::mutex> lock() { return std::unique_lock<std::mutex>(m); }
std::mutex m;
std::condition_variable cv;
std::deque<T> data;
};
one with a limit on the number of entries should have a second condition_variable to notify when someone pops, and push should see if you are at the limit.
Supporting "overwrite when full" becomes another option. A 1-element "most recent" looks like:
template<class T>
struct mono_threadsafe_queue {
// waits until we have data, then returns it.
T pop() {
auto l = lock();
cv_hasdata.wait( l, [&]{ return (bool)data; } );
T r = std::move(*data);
data = boost::none;
cv_hasroom.notify_one();
return r;
}
// waits for there to be room if there is none.
void push( T&& t ) {
auto l = lock();
cv_hasroom.wait( l, [&]{ return !(bool)data; } );
data = std::move(t);
cv_hasdata.notify_one();
}
void replace( T&& t ) {
auto l = lock();
data = std::move(t);
cv_hasdata.notify_one();
}
// replaces data if f returns true, or if there is no data
// imagine data with a timestamp, and we only want to replace it with
// newer data
template<class F>
void replace_if( T&& t, F&& f ) {
auto l = lock();
if (!data || !f(*data))
{
data = std::move(t);
cv_hasdata.notify_one();
}
}
void abandon() {
auto l = lock();
data = boost::none;
cv_hasroom.notify_one();
}
private:
std::unique_lock<std::mutex> lock() { return std::unique_lock<std::mutex>(m); }
std::mutex m;
std::condition_variable cv_hasdata;
std::condition_variable cv_hasroom;
boost::optional<T> data;
};
Upvotes: 1
Reputation: 238311
Which is the best way to yield thread?
It is std::this_thread::yield
.
Now I use
std::this_thread::sleep_for(100ms);
While sleeping does yield the thread as a side-effect, that's not all that it does. As the name implies, it blocks the thread for a given time.
However, it is unclear how yielding or sleeping would be useful in a producer / consumer case such as what you describe. What you probably should do is wait on a condition variable instead.
Upvotes: 6
Reputation: 563
Introduce a wait condition on which your consumer thread needs to wait. Once the data is generated by producer it can notify your consumer thread, till then consumer waits.
Upvotes: 2