Simple use of C + + multithreading

Posted by ionik on Fri, 04 Mar 2022 23:04:08 +0100

For the use of multithreading, this paper briefly introduces several ways of using multithreading, and uses several simple examples to introduce multithreading. The compiler is visual studio.

1, AsyncFuture

The knowledge points used are std::async and std::future

1. std::async function prototype

template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);

Function: the second parameter receives a callable object (imitation function, lambda expression, class member function, ordinary function...) As parameters, and execute them asynchronously or synchronously.

Whether to execute asynchronously or synchronously depends on the execution policy of the first parameter:

(1) The callable object passed by std::launch::async executes asynchronously;

(2) std::launch::deferred: the callable objects passed are executed synchronously;

(3) std::launch::async | std::launch::deferred can be asynchronous or synchronous, depending on the operating system, which is beyond our control;

(4) If no specific policy is specified, it will be executed by default (3).

If an asynchronous execution policy is selected, when get is called, if the asynchronous execution is not finished, get blocks the current calling thread until the end of asynchronous execution ends and gets the result. If the asynchronous execution is finished, it does not wait to get the execution result; If the synchronous execution strategy is selected, the synchronous call is actually executed only when the get function is called, which is also called function call delay.

Status of returned result std::future:

(1) deffered: the asynchronous operation has not started yet;

(2) ready: the asynchronous operation has been completed;

(3) timeout: the asynchronous operation timed out.

example

#include "stdfax.h"

/*
    std::async,std::future   
*/
int main()
{
    // step1  possibly start thread immediately
    std::future<int> ft1(std::async(sum, 1, 11));
    std::future<int> ft2(std::async(sum, 1, 101));

    try {
        // step2  to get result we have to call future.get()
        int a = ft1.get();
        int b = ft2.get();

        cout << a << " + " << b << " = " << a + b << endl;
    }
    catch (std::exception& e)
    {
        cout << e.what() << endl;
    }
    
    return 0;
}

2, PackageTaks

The knowledge points used are std::package_task and std::future

1,std::package_task is a template class

std::packaged_task wraps any callable object (function, lambda expression, bind expression, function object) so that it can be called asynchronously. Its return value or thrown exception is stored in a shared state that can be accessed through the std::future object.

example

#include "stdfax.h"

/*
	std::package_task, std::promise,std::future
*/

int main()
{
   // step 1 does not start thread yet
	std::packaged_task<int(int, int)> task1(sum);
	std::packaged_task<int(int, int)> task2(sum);

	// step2  create future using task
	std::future<int> ft1 = task1.get_future();
	std::future<int> ft2 = task2.get_future();

	// step3 we have to start the thread
	// start the task (or thread)
	task1(1, 11);
	task2(1, 101);

	try {
		// step 4  now get the result of processing
		int a = ft1.get();
		int b = ft2.get();

		cout << a << " + " << b << " = " << a + b << endl;
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
}

3, Promise future

The knowledge points used are std::thread, std::promise and std::future

1,std::future

std::future expects a return. From the perspective of an asynchronous call, future is more like the return value of an executing function. The C + + standard library uses std::future to model a one-time event. If an event needs to wait for a specific one-time event, the thread can obtain a future object to represent the event. Asynchronous calls often do not know when to return, but if the process of asynchronous calls needs to be synchronized, or the latter asynchronous call needs to use the result of the previous asynchronous call. The future will be used at this time.

2,std::promise

Promise object can store the value of a certain type of T, which can be read by the future object (possibly in another program). Therefore, promise also provides a means of thread synchronization. During the construction of promise object, it can be associated with a shared state (usually std::future), and a value of type T can be saved on the associated shared state (std::future).

You can use get_future to obtain the future object associated with the promise object. After calling this function, the two objects share the same shared state

  • promise object is an asynchronous Provider, which can set the value of sharing state at a certain time.
  • The future object can return the value of the shared state asynchronously, or block the caller if necessary and wait for the shared state flag to change to ready before obtaining the value of the shared state.

get_future()

This function returns a future associated with promise shared state. The returned future object can access the value set in the shared state by the promise object or an exception object. Only one future object can be obtained from promise shared state. After calling this function, the promise object is usually ready at a certain point in time (set a value or an exception object). If the value or exception is not set, the promise object will automatically set a future when destructing_ Error exception (broken_promise) to set its own preparation state.

example

#include "stdfax.h"

/*
	std:thread,std::promise,std:future
*/
int main()
{
	// step 1 create a promise with return type
	std::promise<int> prm,prm2;

	// step 2 create a future using the promise
	std::future<int> ft = prm.get_future();
	std::future<int> ft2 = prm2.get_future();

	// step 3 create a thread using promise
	std::thread thr(sum_prm, std::ref(prm), 1, 11);
	std::thread thr2(sum_prm, std::ref(prm2), 1, 101);

	// step 4 detach thread
	// you should not forget this step
	thr.detach();
	thr2.detach();

	try {
		// step 5 now get the result of processing
		int a = ft.get();
		int b = ft2.get();

		cout << a << " + " << b << " = " << a + b << endl;
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

General code of zero and example

stdafx.h

#include <stdio.h>
#include <tchar.h>

#include "thread_inc.h"

stdafx.cpp

#include "stdfax.h"

#include "thread_inc.cpp"

thread_inc.h

#include <iostream>
#include <thread>
#include <memory>
#include <future>
#include <functional>
#include <utility>
#include <exception>
#include <sstream>

using namespace std;   

extern int sum(int st, int ed);
extern void sum_prm(std::promise<int>& prm, int st, int ed);

thread_inc.cpp the code of this file does not participate in the generation. Click thread_inc.cpp, right-click the attribute, and in general - item type, set not to participate in generation.

int sum(int st, int ed)
{
	int rlt = 0;

	for (int i = st; i < ed; ++i)
	{
		// Setting exception
		if (i == (st + ed) / 2)
		{
			std::ostringstream os;
			os << "sum(" <<st<< ", " << ed << ") - throw exception at: " << i;

			throw std::runtime_error(os.str().c_str());
		}
		rlt += i;
	}
	return rlt;
}


void sum_prm(std::promise<int>& prm, int st, int ed)
{
	int rlt = 0;

	try {
		for (int i = st; i < ed; ++i)
		{
			// Setting exception
			if (i == (st + ed) / 2)
			{
				std::ostringstream os;
				os << "sum_prm(" << st << ", " << ed << ") - throw exception at: " << i;

				throw std::runtime_error(os.str().c_str());
			}

			rlt += i;
		}

		// prm.set_value(rlt);
		prm.set_value_at_thread_exit(rlt);
	}
	catch (std::exception& e)
	{
		//prm.set_exception(std::current_exception() )
		prm.set_exception_at_thread_exit(std::current_exception());
	}
}

Topics: C++ Promise async