C ++ multi-threaded asynchronous programming ---

 

The main thread synchronization in order to solve competition problems accessing shared data, so the main thread synchronization is access to shared data synchronization (in accordance with established priorities, a need to block access to start after waiting for a visit before completion). This article talks about asynchronous programming is mainly directed against the order of execution of tasks or threads, that is a task does not need to block waiting on a task to complete before execution begins execution, the order execution order of tasks and procedures are inconsistent. Here the difference between synchronous and asynchronous execution order of tasks from the interpretation angle:

Synchronization: that when issuing a call before not getting the results, the call will not return. But once the call returns, the return value is obtained. In other words, this initiative by the caller to wait for results of the call.
Asynchronous: Called after issue, the call will return directly, so no results are returned. In other words, when an asynchronous procedure call is issued, the caller will not immediately get results. But after the call is issued, the caller through the state, notification to inform the caller, or to deal with this by calling the callback function.


Second, how to use asynchronous programming
method execution thread does not result in the thread library <thread> usually the case, the thread caller needs to get the results or execution state of the thread to perform the next task. So, to get the results or the status of the callee it by what means?

2.1 use of global variables and condition variables to pass the results of
the previous mentioned condition variables with the "notice - wake up" function, you can put the results into the state or perform a global variable, when the caller is executing the task, by notification condition variable the caller result or status has been updated to use. A program example is given below:

// future1.cpp use of global variables passed by the calling thread returns results, use a condition variable notify the calling thread has been the result 

#include <the Vector> 
#include <numeric> 
#include <iostream> 
#include <Chrono> 
#include <the Thread> 
# the include <the mutex> 
#include <condition_variable> int RES = 0 ;                         // save the results of a global variable 
STD :: MU the mutex;                         // mutex global variable 
STD :: condition_variable cond;        // global condition variable void the accumulate (STD: : the Vector < int > :: Iterator First, 
                std :: the Vector


 
< Int > :: Last Iterator) 
{ 
    int SUM = STD :: the accumulate (First, Last, 0 );       // standard library summing function 
    STD unique_lock is :: <STD :: the mutex> Locker (MU); 
    RES = SUM; 
    locker.unlock (); 
    cond.notify_one ();               // notice to a waiting thread "condition has been satisfied" 
} 
 
int main () 
{ 
    STD :: Vector < int > = {Numbers . 1 , 2 , . 3 , . 4 , 5 , 6 };
    Thread work_thread :: STD (the accumulate, numbers.begin (), numbers.end ()); 

    STD unique_lock is :: <STD :: the mutex> Locker (MU); 
    cond.wait (Locker, [] () { return RES; });    // if the condition variable wakes up, check whether the result is changed, the direct return to true, false continue to wait 
    std :: cout << " the result = " << RES << ' \ the n- ' ; 
    Locker. UNLOCK (); 
    work_thread.join ();          // waiting thread to complete execution 
 
    getchar (); 
    return  0 ; 
}

Function is performed as follows:
future1 execution results

As can be seen from the above code, although the results achieved to obtain asynchronous task execution functions, but needs more global variables, the degree of coupling between multiple threads is higher, easily introduced bug when writing complex programs. Is there a better way to implement asynchronous programming it? C ++ 11 adds a <future> library function provides a great convenience for asynchronous programming.

2.2 and future promise delivery result
<future> header values for a particular feature allows providers asynchronous access is provided, may be in different threads.
These providers (either promise objects, either packaged_task objects, or asynchronous calls to async) and future sharing of objects to share state: the shared state providers ready access point and the target point shared future state synchronize. <Future> header structure is as follows:

Observe the above status shared among multiple threads return value or an exception is thrown in a shared state in exchange of transfer. We know that among multi-threaded concurrent access to shared data is needed to keep pace, where the shared state is the key to the return value or exception between threads delivered correctly to ensure, by the calling thread can notify the calling thread by changing the shared status return value or an exception has been written the complete, you can access or operate. future state (future_status) has the following three:

deferred: asynchronous operation has not yet begun;
READY: asynchronous operation has been completed;
timeout: asynchronous operation timed out.
Since the threads pass the return value or exception is through shared state, involving the sharing status of the provider and the acquirer, only the task or thread owns an object that contains the shared state, other tasks or threads to be able to inform through shared state synchronization mechanism to get the person or thread return value or an exception. We usually use the <thread> Create a thread does not have shared state, we need to provide a shared state for that thread, so that subsequent to its return value or exception of access. So, how to provide an object that contains a shared state it is a thread? This requires the help std :: promise <T> class template implements, which are used as follows:

std::promise< T >构造时,产生一个未就绪的共享状态(包含存储的T值和是否就绪的状态)。可设置T值,并让状态变为ready。也可以通过产生一个future对象获取到已就绪的共享状态中的T值。继续使用上面的程序示例,改为使用promise传递结果,修改后的代码如下:

//future2.cpp 使用promise传递被调用线程返回结果,通过共享状态变化通知调用线程已获得结果

#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
 
void accumulate(std::vector<int>::iterator first,
                std::vector<int>::iterator last,
                std::promise<int> accumulate_promise)
{
    int sum = std::accumulate(first, last, 0);
    accumulate_promise.set_value(sum);  // 将结果存入,并让共享状态变为就绪以提醒future
}
 
int main()
{
    // 演示用 promise<int> 在线程间传递结果。
    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
    std::promise<int> accumulate_promise;
    std::future<int> accumulate_future = accumulate_promise.get_future();
    std::thread work_thread(accumulate, numbers.begin(), numbers.end(),
                            std::move(accumulate_promise));
    accumulate_future.wait();  //等待结果
    std::cout << "result=" << accumulate_future.get() << '\n';
    work_thread.join();  //阻塞等待线程执行完成
 
    getchar();
    return 0;
}

std::promise< T >对象的成员函数get_future()产生一个std::future< T >对象,代码示例中已经展示了future对象的两个方法:wait()与get(),下面给出更多操作函数供参考:

值得注意的是,std::future< T >在多个线程等待时,只有一个线程能获取等待结果。当需要多个线程等待相同的事件的结果(即多处访问同一个共享状态),需要用std::shared_future< T >来替代std::future < T >,std::future< T >也提供了一个将future转换为shared_future的方法f.share(),但转换后原future状态失效。这有点类似于智能指针std::unique_ptr< T >与std::shared_ptr< T >的关系,使用时需要留心。

2.3使用packaged_task与future传递结果

除了为一个任务或线程提供一个包含共享状态的变量,还可以直接把共享状态包装进一个任务或线程中。这就需要借助std::packaged_task< Func >来实现了,其具体用法如下:

std::packaged_task< Func >构造时绑定一个函数对象,也产生一个未就绪的共享状态。通过thread启动或者仿函数形式启动该函数对象。但是相比promise,没有提供set_value()公用接口,而是当执行完绑定的函数对象,其执行结果返回值或所抛异常被存储于能通过 std::future 对象访问的共享状态中。继续使用上面的程序示例,改为使用packaged_task传递结果,修改后的代码如下:

//future3.cpp 使用packaged_task传递被调用线程返回结果,通过共享状态变化通知调用线程已获得结果

#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
 
int accumulate(std::vector<int>::iterator first,
                std::vector<int>::iterator last)
{
    int sum = std::accumulate(first, last, 0);
    return sum;
}
 
int main()
{
    // 演示用 packaged_task 在线程间传递结果。
    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
    std::packaged_task<int(std::vector<int>::iterator,std::vector<int>::iterator)> accumulate_task(accumulate);
    std::future<int> accumulate_future = accumulate_task.get_future();
    std::thread work_thread(std::move(accumulate_task), numbers.begin(), numbers.end());
    accumulate_future.wait();  //等待结果
    std::cout << "result=" << accumulate_future.get() << '\n';
    work_thread.join();  //阻塞等待线程执行完成
 
    getchar();
    return 0;
}

一般不同函数间传递数据时,主要是借助全局变量、返回值、函数参数等来实现的。上面第一种方法使用全局变量传递数据,会使得不同函数间的耦合度较高,不利于模块化编程。后面两种方法分别通过函数参数与返回值来传递数据,可以降低函数间的耦合度,使编程和维护更简单快捷。

2.4 使用async传递结果
前面介绍的std::promise< T >与std::packaged_task< Func >已经提供了较丰富的异步编程工具,但在使用时既需要创建提供共享状态的对象(promise与packaged_task),又需要创建访问共享状态的对象(future与shared_future),还是觉得使用起来不够方便。有没有更简单的异步编程工具呢?future头文件也确实封装了更高级别的函数std::async,其具体用法如下:

std::future std::async(std::launch policy, Func, Args…)
std::async是一个函数而非类模板,其函数执行完后的返回值绑定给使用std::async的std::futrue对象(std::async其实是封装了thread,packged_task的功能,使异步执行一个任务更为方便)。Func是要调用的可调用对象(function, member function, function object, lambda),Args是传递给Func的参数,std::launch policy是启动策略,它控制std::async的异步行为,我们可以用三种不同的启动策略来创建std::async:

std::launch::async参数 保证异步行为,即传递函数将在单独的线程中执行;
std::launch::deferred参数 当其他线程调用get()/wait()来访问共享状态时,将调用非异步行为;
std::launch::async | std::launch::deferred参数 是默认行为(可省略)。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载。
继续使用上面的程序示例,改为使用std::async传递结果,修改后的代码如下:

//future4.cpp 使用async传递被调用线程返回结果

#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
 
int accumulate(std::vector<int>::iterator first,
                std::vector<int>::iterator last)
{
    int sum = std::accumulate(first, last, 0);
    return sum;
}
 
int main()
{
    // 演示用 async 在线程间传递结果。
    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
    auto accumulate_future = std::async(std::launch::async, accumulate, numbers.begin(), numbers.end());        //auto可以自动推断变量的类型
    std::cout << "result=" << accumulate_future.get() << '\n';
 
    getchar();
    return 0;
}

As can be seen from the above code can use std :: async less programming effort largely simplified, so that we do not focus on the details of the internal thread creation, will be able to easily access asynchronous execution status and results, you can also specify the thread creation strategy . So, we can use std :: async alternative to creating a thread, let it be our first choice to do asynchronous operation.

 

Guess you like

Origin www.cnblogs.com/lx17746071609/p/11128255.html