std::future使用

安稳与你 提交于 2021-01-29 00:21:57

一、std::future作为类模板,包装对象。

当多个线程共享一个全局变量的时候,需要使用锁、条件变量等机制。std::future 可以充当全局变量、锁、条件变量完成线程之间数据共享。std::future是线程安全的。

std::future对象可以和asych,std::packaged_task,std::promise一起使用。这篇文章集中讨论std::future和std::promise。

我们经常会遇到需要得到线程返回结果的情况,现在的问题是我们如何实现。

举个例子:

假设在程序中,我们创建了一个压缩给定文件夹的线程,并且我们希望该线程能够返回新的zip文件的名称和大小。有两种实现方式:

1、老方法:使用指针在线程间共享数据

传递一个指针到新的线程中,该线程将在其中设置数据。直到主线程继续等待使用条件变量。当新线程设置数据并通知条件变量时,主线程将唤醒并从该指针处获取数据。

为了实现这一简单功能,我们使用了一个条件变量、一个mutex锁和一个指针,来实现捕获返回值。

如果我们想要该线程在不同的时间点返回3个不同的值,问题会变得更加复杂,有没有一种简单的方法来从线程处获取返回值呢?答案是使用std::future

2、c++11的方法:使用std::future 和 std::promise

std::future是一个类模板(class template),其对象存储未来的值,

到底什么是未来的值呢?

事实上,一个std::future对象在内部存储一个将来会被赋值的值,并提供了一个访问该值的机制,通过get()成员函数实现。但如果有人视图在get()函数可用之前通过它来访问相关的值,那么get()函数将会阻塞,直到该值可用。std::promise也是一个类模板,其对象有可能在将来对值进行赋值,每个std::promise对象有一个对应的std::future对象,一旦由std::promise对象设置,std::future将会对其赋值。std::promise对象与其管理的std::future对象共享数据。

逐步解析

在线程1中创建一个std::promise对象

std::promise<int> promiseObj;目前为止,该promise对象没有任何管理的值,但它承诺肯定会有人对其进行赋值,一旦被赋值,就可以通过其管理的std::future对象来获取该值。

但是,假设线程1创建了该promise对象并将其传给线程2,那么线程1怎样知道线程2什么时候会对promise对象进行赋值呢?答案是使用std::future对象

每个std::promise对象都有个对应的std::future对象,其他人可以通过它来获取promise设置的值。

所以,线程1将会创建std::promise对象,然后在将其传递给线程2之前从它那里获取std::future对象。std::future<int> futureObj = promiseObj.get_future();现在,线程1将promiseObj传递给线程2.

那么线程1将会通过std::future的get函数获取到线程2设置在std::promise中的值,int val = futureObj.get();但是如果线程2还没有对该值进行设置,那么这个调用将会阻塞,直到线程2在promise对象中对该值进行设置。promiseObj.set_value(45);查看下图中的完整流程

看一个完整的std::future和std::promise的例子:

#include <iostream>

#include <thread>

#include <future>

void initiazer(std::promise<int>* promObj)

{

    std::cout<<"Inside Thread"<<std::endl; promObj->set_value(35);

}

int main()

{

    std::promise<int> promiseObj;

    std::future<int> futureObj = promiseObj.get_future();

    std::thread th(initiazer, &promiseObj);

    std::cout<<futureObj.get()<<std::endl;

    th.join();

    return 0;

}

如果std::promise对象在赋值之前被销毁,那么管理的std::future对象上的get()调用将会抛出异常。

除此之外,如果想要线程在不同时间点返回多个值,只需要在线程中传输多个std::promise对象,并从相关的多个std::future对象中获取多个返回值。

二、std::function作为函数包装器,指代一系列返回值类型一致,参数一致的函数。

• 类模板声明

template <class T> function; // undefined template <class Ret, class... Args> class function<Ret(Args...)>;

• 类模板说明

std::function是一个函数包装器模板,最早来自boost库,对应其boost::function函数包装器。在c++0x11中,将boost::function纳入标准库中。该函数包装器模板能包装任何类型的可调用元素(callable element),例如普通函数和函数对象。包装器对象可以进行拷贝,并且包装器类型仅仅只依赖于其调用特征(call signature),而不依赖于可调用元素自身的类型。

一个std::function类型对象实例可以包装下列这几种可调用元素类型:函数、函数指针、类成员函数指针或任意类型的函数对象(例如定义了operator()操作并拥有函数闭包)。std::function对象可被拷贝和转移,并且可以使用指定的调用特征来直接调用目标元素。当std::function对象未包裹任何实际的可调用元素,调用该std::function对象将抛出std::bad_function_call异常

• 模板参数说明

**T      : 通用类型,但实际通用类型模板并没有被定义,只有当T的类型为形如Ret(Args...)**的函数类型才能工作。

**Ret  ** : 调用函数返回值的类型。

Args : 函数参数类型。

std::function详解

• 包装普通函数

#include <iostream>

#include <functional>

using namespace std;

int g_Minus(int i, int j)

{

    return i - j;

}

int main()

{

    function<int(int, int)> f = g_Minus; cout << f(1, 2) << endl; // -1

    return 1;

}

• 包装模板函数

#include <iostream>

#include <functional>

using namespace std;

template <class T>

T g_Minus(T i, T j)

{

    return i - j;

}

int main()

{

    function<int(int, int)> f = g_Minus<int>;

    cout << f(1, 2) << endl; // -1

    return 1;
}

• 包装lambda表达式

#include <iostream>

#include <functional>

using namespace std;

auto g_Minus = [](int i, int j){ return i - j; };

int main()

{

    function<int(int, int)> f = g_Minus;

    cout << f(1, 2) << endl; // -1

    return 1;

}

• 包装函数对象

非模板类型:

#include <iostream>

#include <functional>

using namespace std;

struct Minus {

  int operator() (int i, int j)

  {

    return i - j;

  }

};

int main()

{

    function<int(int, int)> f = Minus();

    cout << f(1, 2) << endl; // -1

    return 1;

}

模板类型:

#include <iostream>

#include <functional>

using namespace std;

template <class T> struct Minus {

T operator() (T i, T j)

{

    return i - j;

}

}; 

int main() 
{ 
    function<int(int, int)> f = Minus<int>(); 
    cout << f(1, 2) << endl; // -1 
    return 1; 
}

• 包装类静态成员函数

非模板类型:

#include <iostream> 
#include <functional> 
using namespace std; 

class Math { 
public: 
    static int Minus(int i, int j) 
    { 
      return i - j; 
    } 
}; 

int main() 
{ 
    function<int(int, int)> f = &Math::Minus; 
    cout << f(1, 2) << endl; // -1 
    return 1; 
}

模板类型:

#include <iostream> 
#include <functional> 
using namespace std; 

class Math { 
public: 
    template <class T> 
    static T Minus(T i, T j) 
    { 
      return i - j; 
    } 
}; 

int main() 
{ 
    function<int(int, int)> f = &Math::Minus<int>; 
    cout << f(1, 2) << endl; // -1 
    return 1; 
}

• 包装类对象成员函数

非模板类型:

#include <iostream> 
#include <functional> 
using namespace std; 

class Math { 
public: 
    int Minus(int i, int j) 
    { 
      return i - j; 
    } 
}; 

int main() 
{ 
    Math m; 
    function<int(int, int)> f = bind(&Math::Minus, &m, placeholders::_1, placeholders::_2); 
    cout << f(1, 2) << endl; // -1 
    return 1;
}

模板类型:

#include <iostream> 
#include <functional> 
using namespace std; 

class Math { 
public: 
    template <class T> 
    T Minus(T i, T j) 
    { 
      return i - j; 
    } 
}; 

int main() 
{ 
    Math m; 
    function<int(int, int)> f = bind(&Math::Minus<int>, &m, placeholders::_1, placeholders::_2); 
    cout << f(1, 2) << endl; // -1 
    return 1; 
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!