Chapter 4 synchronized concurrent operation

4.1 wait for events or other conditions

We do things with, always should be a prerequisite for the completion of the case, we will begin the next step, so we can choose the following ways:

1. real-time view of the completion of the prerequisites. This will be the next task in a timely manner, but such an operation is a very waste matter of time, during which could have been doing other things, real-time attention now due to the completion of the preconditions had been focused on this thing.

2. At regular intervals to see the completion of preconditions. So although we used to do some other things in these intermittent time, however, this query is not conducive to timely next task Prerequisite.

3. Let the people before the completion of the conditions, notify you after the completion of the task. This approach avoids wasting time in the middle, can also be carried out next task in time, is a very effective strategy.

It marked the condition variable C ++ library wait condition is the third case.

4.1.1 wait condition with a condition variable

C ++ standard library provides an implementation of two conditions variables: std :: condition_variable and std :: condition_variable_any. The former std :: condition_variable only valid with std :: mutex, std :: condition_variable_any but the latter can become anything similar review with the minimum standards mutually exclusive elements work together, but in size, performance, and operating system resources has an extra price. The following shows the use std :: condition_variable.

#include<iostream>
#include<condition_variable>
#include<mutex>
#include<chrono>
#include<thread>

using namespace std;

mutex m;
condition_variable cv;
bool flg;


void pre_print(){
	
	cout<<"pre_print"<<endl;
	flg = true;
	cv.notify_one();
}

void after_print(){
	unique_lock<mutex> lg(m);
	cv.wait(lg, []{return flg;});
	cout<<"after_print"<<endl;
}


int main(){
	flg = false;
	thread t1(after_print), t2(pre_print);
	t1.detach();
	
	//std::this_thread::sleep_for(std::chrono::seconds(3));

	t2.join();
	cout<<"finish_process"<<endl;
	return 0;
}

4.1.2 Use condition variables to create a thread-safe queue

For the queue to write a secure support multithreaded concurrent data structures --safe_queue.

Have time to fix the relevant code

4.2 future waiting for the next event

For example, when calling a function to get returns, sometimes that happens only once in a single thread is easy to solve, in a multi-threaded, we must rely on the C ++ standard library to resolve the future, though future support for multi-threaded communication, but this tool does not support synchronous access, if multiple threads need to synchronize access to the same future you should use a mutex yuan to the settlement.

In C ++ regarding future in two ways:

1.future: The only instance when a future instance of its associated events point.

2.shared_future: shared_future can point to multiple instances of the same event.

4.2.1 The return value from the background task

When using multiple threads, we will think of thread, but this does not provide a mechanism to receive the return value of the properties, so now we have to learn another function templates for multi-threaded, std :: async, use this function and thread similar, but the results return a std :: future of the object, not a thread object lets you wait, the future holds the object eventually return value of the function of multi-threaded, would only need to use the get function on the future function of the object can be obtained return value. E.g

#include<thread>
#include<future>
#include<iostream>
#include<vector>
using namespace std;

vector<int> add(){
	vector<int> ans;
	for(int i = 0; i < 10; ++i)
		ans.push_back(i);
	return ans;
}


int main(){
	future<vector<int>> res = async(add);
	vector<int> ans = res.get();
	for(auto i: ans)
		cout<<i<<' ';
	cout<<endl;
	return 0;
}

While the above example can be solved with a single thread, but the above example except for the use of the function described. Multithreading async method calls can also be specified, async (std :: launch :: deferred indicate that the call has been delayed until use future.wait () or future.get () so far, but async (std :: launch :: async ) indicates that must run on his own thread (Note: the wait (according to textbook words) run or get (), I feel it should be run in this_thread in, just wait or get the function again start the thread, if the master does not want to correct me).

4.2.2 The tasks associated with the future

Standard C ++ class library have std :: package_task <> may be bound to the future or a callable function on the object, when std :: package_task <> is called, the calling function may be bound or object is also invoked , and the future into the ready state, the return value is stored as associated data. When std :: package_task <> is called as a callable object, there is provided the function call to the function parameters are passed to the operator contained, and the result is returned as a result of induction, obtained from the std get_future () stores: : future in. Example of use as book

#include<deque>
#include<mutex>
#include<future>
#include<thread>
#include<utility>

using namespace std;

mutex m;
deque<package_task<void()>> tasks;

bool gui_shutdown_message_received();
void get_and_process_gui_message();

void gui_thread(){
	while(!gui_shutdown_message_received()){
		std::package_task<void()> task;
		{
			lock_guard<mutex> lg(m);
			if(tasks.empty())
				continue;
			task = std::move(task.front());
			task.pop_front();
		}
		task();
	}		
}	

std::thread gui_bg_thread(gui_thread);
template<typename Func>
std::future<void> post_task_for_gui_thread(Func f){
		package_task<void()> task(f);
		future<void> res = task.get_future();
		lock_guard<mutex> lg(m);
		tasks.push_back(std::move(task));
		return res;
} 

4.2.3 generated std :: promise

In my opinion is a tool std :: promise for communication between threads, std :: promise <T> provide a way to set the value (type T), he can after that by an associated std :: future <T> object can be read. One pair of std :: promise / std :: future provide a possible mechanism for this facility, the waiting thread can block future, while providing thread data can be used to pair the promise to set the relevant values ​​and future ready.

At the same time you can use get_future () member function to get the std :: future objects associated with a given std :: promise of. After setting value when the promise (using the set_value () member function) ,, ,, Future will become ready and may be used to obtain the value stored in the lock. If the value is not set to destroy std :: promise, it will be stored in an exception. Examples are as follows

#include<iostream>
#include<future>
#include<condition_variable>
#include<chrono>
#include<thread>
#include<mutex>

using namespace std;

promise<int> ps;
mutex m;
condition_variable cv;
bool flg;

void get_and_show_val(){
	int t;
	
	unique_lock<mutex> ul(m);
	cv.wait(ul, [](){return flg;});
	flg = false;
	future<int> ans = ps.get_future();
	t = ans.get();
	cout<<t<<endl;

}

void set_val(){


	unique_lock<mutex> ul(m);
	ps.set_value(3);
	flg = true;
	cv.notify_one();
	ul.unlock();
	this_thread::sleep_for(chrono::seconds(3));
		

}

int main(){
	flg = false;
	thread t1(get_and_show_val), t2(set_val);
	t1.detach();
	t2.join();
	return 0;
}		 

According https://zh.cppreference.com/w/cpp/thread/promise  shows that the promise should only be used once.

4.2.4 save for the future abnormal

Promise course also be used, but the use of set values ​​when set_exception (), instead set_value ().

std::promise<double> some_promise;
try{
	some_promise.set_val(calc_val());
}
catch{
	some_promise.set_exception(std::current_exception());
}

4.2.5 from multiple threads waiting

When multiple threads access the object std :: furture without additional synchronization, data competition and undefined behavior occurs, it is intentional, the result of future ownership unified asynchronous model, and get () is single such is the nature of concurrent access does not make sense - only one thread can get the value, because after the first call to get (), there would be no value left available, but at this time we can use the std std :: shared_future :: future hand over ownership to achieve the effect can be replicated, there are several ways you can use shared_future

future<int> f;
share_future<int> sf(f);
//或者
// share_future<int> sf = f.share();

4.3 have time to wait

About overtime in two ways:

1. Based on the time period: waiting for a specified length of time. Functional form for the _for

2. Based on the time point (absolute timeout): point until the appointed time. Functional form for the _until

4.3.1 Clock

A source of clock time information, specifically, the following four classes clock different parts of the information.

1. Time now

2. Get the type used to represent the time from the clock to the value

3. The period of the clock beat

4. clocks are clocked at a uniform rate, decide whether it is a uniform (Steady) Clock

For a specific point in time type is typedef time_point members

4.3.2 period

Period of time is the easiest part of the time in support of, they are <> for processing by std :: chrono :: duration, the first representative of the type template, the second template is a fraction, on behalf of each time period indicates how many seconds , short storage of a few minutes as expressed std :: chrono :: duration <short, std :: radio <60,1 >>, double storage of several milliseconds is represented as std :: chrono :: duration <double, std: : radio <1,1000 >> 1 ms represented 1/1000 sec

In the case without truncation, the transition between the period implicit (thus converting the second hour is possible, it will be converted to the second hour is not the case), by an explicit conversion std :: chrono :: duration_cast <> to convert.

4.3.3 point in time

Time is by std :: chrono :: time_point <> class template management.

4.3.4 reception timeout function

Classes / namespaces function return value
namespace std :: this_thread

 sleep_for(duration)

sleep_until(time_point)

unavailable

std :: condition_variable or

std::condition_variable_any

wait_for(lock,duration)

wait_until(lock, time_point) 

std::cv_status::timeout或

std::cv_status::no_timeout

 

wait_for(lock, duration, predication)

wait_unitl(lock, time_point, predition) 

bool-- when waking predicate return value

std :: time_mutex or

std::recursize_time_mutex

try_lock_for(duration)

try_lock_until(time_point) 

bool - true if access to the lock, otherwise false
std::unique_lock<TimedLockable>

unique_lock(lockable, duration)

unique_lokc(lockable, time_point) 

Unavailable --owns_lock () on the newly constructed object, such as a lock or obtained returns true, false otherwise
 

try_lock_for(duration)

try_lock_until(time_point) 

bool-- If you get a lock, false otherwise

std::future<ValueType>或者

std::shared_future<ValueType>

wait_for(duration)

wait_until(time_point) 

std :: future_status :: timeout if the wait timeout,

std :: future_status :: ready ready or if future

std :: future_status :: deferred if the future hold delay function has not yet begun

4.4 simplify code synchronization operation using

4.4.1 functional programming with the future

4.4.2 synchronization with messaging

Guess you like

Origin www.cnblogs.com/hebust-fengyu/p/12087822.html