C++ Threads and Future, Promise Templates

Posted by phwae on Fri, 12 Nov 2021 21:56:25 +0100

stay Multithreaded In programming, data sharing is required, such as when one thread needs to use data generated by another thread's operations.

When asynchronous programming is involved, thread-to-thread synchronization is sometimes required, for example, when an asynchronous subscription message is executed, the thread that processes the message does not need to run until the result of the subscription message returns successfully.

Both of the above scenarios can be implemented using std::future and std::promise

Example 1

#include<iostream>
#include<future>
#include<mutex>
#include<chrono>
#include<thread>
#include<stdlib.h>
using namespace std;
void Thread_Fun1(std::promise<int> &p)
{
	//For effect, allow threads to sleep for 5s
	// std::this_thread::sleep_for(std::chrono::seconds(5));
 
	int iVal = 233;
	std::cout << "incoming data(int): " << iVal << std::endl;
 
	//Incoming data iVal
	p.set_value(iVal);
} 
void Thread_Fun2(std::future<int> &f)
{
	//Block the function until you receive data from the associated std::promise object
	auto iVal = f.get();		//iVal = 233
 
	std::cout << "Received data(int): " << iVal << std::endl;
}
 
int main()
{
	//Declare an std::promise object pr1, which holds values of type int
	std::promise<int> pr1;
	//Declare an std::future object fu1 and pass get_of std::promise Future() function bound to pr1
	std::future<int> fu1 = pr1.get_future();
 
	//Create a thread t1 that will function Thread_Fun1 and object pr1 are executed in a thread//std::ref, which means we want to pass the variable itself
	std::thread t1(Thread_Fun1, std::ref(pr1));
	//Create a thread t2 that will function Thread_Fun2 and object fu1 execute in a thread
	std::thread t2(Thread_Fun2, std::ref(fu1));
 
	//Block to end of thread
	t1.join();
	t2.join();

	system("pause");
 
	return 0;
}

Execution results:

Incoming data (int):233
Received data (int):233

Multithread Shared Value

Steps:

  1. Create a std::promise object in thread A: std::promise<int> proObj;
  2. Associate std::promise with std::future:std::future<int> futObj = proObj.get_ Future();
  3. Pass the proObj object to another thread B, which sets its value at the appropriate time: proObj.set_value(23);
  4. Read the value of the object through std::future in thread A: int val = futObj.get();

Thread A is always safe to get regardless of when Thread B sets the value of the object. One thing to note is that when you get a value in A through futObj.get(), if B is not set, A will block until the value is set and obtained successfully.

Example 2

#include <iostream>
#include <thread>
#include <future>
#include <chrono>
 
// Thread B
void initiazer(std::promise<int> * promObj)
{
    std::cout << "Thread B" << std::endl;
    // set the value at proper time
    std::this_thread::sleep_for(std::chrono::seconds(3));
    promObj->set_value(23);
}
 
int main()
{
		// Thread A
    std::promise<int> promiseObj;
    std::future<int> futureObj = promiseObj.get_future();
    
    std::thread th(initiazer, &promiseObj); // Start Thread B
	// std::thread th(initiazer, std::ref(promiseObj)); // Start Thread B is required for the above function parameters &Reference
    
    // Gets the value of the object, which returns 23 after B sets its value and blockages before B sets its value
    std::cout<< futureObj.get() << std::endl;
    
    th.join();
    
    return 0;
}

  Execution results:

Thread B
23

Inter-Thread Synchronization

You can use the following code to determine if the value of the shared object is ready:

template<typename T>
bool is_ready(std::future<T> const& f)
{ return f.wait_for(std::chrono::seconds(3)) == std::future_status::ready; }

Wait_of future object The for function blocks waiting for the result to become available, and the available flags are one of two cases:

  • Set time-out
  • The state of the shared object changes to ready

Its prototype is as follows:

template< class Rep, class Period >
std::future_status wait_for( const std::chrono::duration<Rep,Period>& timeout_duration ) const;

The return value identifies the status of the result as:

  • future_status::deferred: The function of the calculated result is not started
  • future_status::ready: result ready
  • future_status::timeout: timeout

Example 3

#include <iostream>
#include <thread>
#include <future>
#include <chrono>
 
// Thread B
void loginSrv(std::promise<bool> * promObj)
{
    std::cout << "Thread B" << std::endl;
    // set the value
    std::this_thread::sleep_for(std::chrono::seconds(3));
    // if (loginSucc)
    // 		promObj->set_value(true);
    // else
    		promObj->set_value(false);
}
 
int main()
{
		// Thread A
    std::promise<bool> promiseObj;
    std::future<bool> futureObj = promiseObj.get_future();
    
    std::thread th(loginSrv, &promiseObj); // Start Thread B
    
    // Blocks 8s waiting for logon results and times out if no results are returned within 8s
    if (futureObj.wait_for(std::chrono::seconds(8)) == std::future_status::ready)
       	if (futureObj.get()) // Return login results, no more blocking
       			std::cout<<"doLoginSuccThings"<< std::endl;
       	else
       			std::cout<<"doLoginFailedThings"<< std::endl;
    else
        // Timeout failed to log in successfully
        std::cout<<"doOtherThings"<< std::endl;
    
    th.join();
    system("pause");
    return 0;
}

Execution results:

Thread B
doLoginFailedThings

Summary:

  1. promise and future enable fast data sharing and synchronization between threads.
  2. promise provides a value and changes it later (set_value).
  3. future is associated with promise, and the thread securely gets the value (get).
  4. Thread synchronization can be achieved by utilizing the blocking wait feature of future (wait_for).

Topics: C++