zoukankan      html  css  js  c++  java
  • std::thread,std::future,std::promise,std::async

    中文文档:原子操作std::atomic(可以无锁并发编程)

    中文文档:线程支持库

    参考:C++:线程(std::thread)

    转载:C++多线程

    一、创建线程

    #include <iostream>
    #include <thread>
    
    void fun1()
    {
    	for (int i = 0; i < 10; i++)
    	{
    		std::cout << "1" << "2" << std::endl;
    	}
    }
    void fun2()
    {
    	for (int i = 0; i < 10; i++)
    	{
    		std::cout << "3" << "4" <<  std::endl;
    	}
    }
    int main() 
    {
    	std::cout << "main start" << std::endl;
    	std::thread t1(fun1);
    	std::thread t2(fun2);
    	std::cout << "main end" << std::endl;
    
    	return 0;
    }
    

    结果
    可以看到,这样创建线程是有问题的,因为在创建了线程后线程开始执行,但是主线程main()并没有停止脚步,仍然继续执行然后退出,此时线程对象还是joinable(可结合的)的,线程仍然存在但指向它的线程对象已经销毁,所以会抛出异常。
    如何保证子线程执行完了退出后再退出主线程呢?

    1.1 解决方法1:join()

    使用join接口可以解决上述问题,join的作用是让主线程等待直到该子线程执行结束(因此调用join会阻塞)。需要注意的是线程对象执行了join后就不再joinable了,所以只能调用join()一次。创建了线程之后并不是调用join或detach才会执行线程,也许调用join或detach之前线程已经执行完了。只要创建了线程对象(传递“函数名/可调用对象”作为参数的情况下),线程就开始执行(std::thread 有一个无参构造函数重载的版本,不会创建底层的线程,只是一个空的对象。)。

    • join阻塞代码演示
    #include <iostream>
    #include <thread>
    #include <chrono>
    #include <vector>
    
    using namespace std;
    
    void work()
    {
    	this_thread::sleep_for(chrono::seconds(5));
    }
    
    int main()
    {
    	clock_t start = clock();
    	vector<thread> t;
    	for (int i = 0; i < 5; i++)
    	{
    		t.emplace_back(thread(work));
    		//t[i].join();  //join放在这里程序运行时间为25s多一点
    	}
    	for (int i = 0; i < 5; i++)
    	{       //join放在这里程序运行时间为5s多一点
    		t[i].join();  //join会阻塞住,因此需要放在创建线程的循环外边才能达到多线程的效果
    	}
    	cout << clock() - start;
    }
    
    • join使用演示
    int main() 
    {
    	std::cout << "main start" << std::endl;
    	std::thread t1(fun1);
    	std::thread t2(fun2);
    	std::cout << "***" << std::endl;
    	t1.join();
    	std::cout << "@@@" << std::endl;
    	t2.join();
    	std::cout << "main end" << std::endl;
    
            //std::thread(fun1).join();  //和上面两句代码等价
    	return 0;
    }
    

    1.2 解决方法2:detach()

    detach是用来和线程对象分离的,这样线程可以独立地执行,不过这样由于没有thread对象指向该线程而失去了对它的控制,当对象析构时线程会继续在后台执行,但是当主程序退出时并不能保证线程能执行完。如果没有良好的控制机制或者这种后台线程比较重要,最好不用detach而应该使用join。

    int main() 
    {
    	std::cout << "main start" << std::endl;
    	std::thread t1(fun1);
    	std::thread t2(fun2);
    	std::cout << "***" << std::endl;
    	t1.detach();
    	std::cout << "@@@" << std::endl;
    	t2.detach();
    	std::cout << "main end" << std::endl;
    
    	return 0;
    }
    

    由结果可见线程并没有执行完而退出

    二、线程调用特殊函数

    2.1 线程调用的函数含有参数

    std::move,std::ref,引用

    多线程中的函数参数如果为引用必须使用std::ref(函数式编程的参数默认使用拷贝方式),多线程中的函数参数如果为IO(socket应该也需要,没有测试过)必须使用移动语义(std::move),避免多个对象同时读写同一个IO缓冲

    #include <thread>
    #include <iostream>
    
    void fun(int& num)  //参数为int&
    {
        while (num < 10)
            std::cout << num++;        
    }
    
    int main()
    {
        int num = 0;
        std::thread t1(fun, std::ref(num));
        std::thread t2(fun, std::ref(num));
        t1.join();
        t2.join();
    
        return 0;
    }
    

    2.2 线程调用成员函数

    #include <iostream>
    #include <thread>
    
    class A
    {
    public:
    	void display(int a) { std::cout << a << '
    '; }
    };
    
    int main()
    {
    	A a;
    	std::thread t(&A::display, a, 3);  //第一个参数必须带&,第二个可带可不带,第二个参数之后是调用函数的实参
    	t.join();
    }
    

    三、多线程执行含有返回值的函数,获取函数返回值

    转载:C++ 中 async、packaged_task、promise 区别及使用

    • 将函数的返回值设置为输出参数

    • 使用std::future、std::promise和packaged_task

    • 使用lambda表达式获取函数返回值(如果线程执行太慢,主线程执行cout的时候result没有计算出来会出问题)。

    lambda表达式

    #include <iostream>
    #include <thread>
    
    int f(int a, int b)
    {
    	return a + b;
    }
    
    int main()
    {
    	int result = 0;
    
    	std::thread* t;
    	t = new std::thread([&] { result = f(2, 3); });
    	t->join();
    
    	std::cout << result;  //result = 5
    	return 0;
    }
    

    std::async和std::future的使用

    转载:C++STL 线程:Future, Promise and async()

    std::async()与std::thread()最明显的不同就是async只是创建异步任务,不一定创建线程。async()默认创建线程,可以设置第一个参数来决定是否创建线程。

    • async函数原型:
    std::async(std::launch::deferred,func,...) //不创建线程,直到调用get()在主线程执行调用的入口函数
    std::async(std::launch::async,func,...) //会创建线程,且立即执行
    std::async(std::launch::async | std::launch::deferred,func,...) //和std::launch::async一样,创建线程
    std::async(func,...) //和std::launch::async一样,创建线程
    
    • 获取函数返回值示例代码:
    //async的使用,配合future直接获取多线程中所调用函数的返回值
    #include <iostream>
    #include <thread>
    #include <future>
    
    int f(int a, int b)
    {
    	using namespace std::chrono_literals;
    	std::this_thread::sleep_for(5s);  //睡眠五秒
    	return a + b;
    }
    
    int main()
    {
    	std::future<int> retVal = std::async(f, 2, 4);
    	std::cout << "start" << '
    ';
    	std::cout << retVal.get();  //会阻塞,即主线程需要子线程执行完从而得到返回值
    	std::cout << "end" << '
    ';
    
    	return 0;
    }
    
    //async,future析构阻塞问题
    #include <iostream>
    #include <thread>
    #include <future>
    
    int main()
    {
    	auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
    	clock_t start = clock();
    	{
    		std::async(std::launch::async, sleep, 5); // 临时对象被析构,阻塞 5s
    		std::async(std::launch::async, sleep, 5); // 临时对象被析构,阻塞 5s
    
    		//auto f1 = std::async( std::launch::async, sleep, 5 );
    		//auto f2 = std::async( std::launch::async, sleep, 5 );
    	}
    	std::cout << (clock() - start) / CLOCKS_PER_SEC << std::endl;
    	return 0;
    }
    

    std::promise和std::future的使用

    转载C++之future和promise

    promise作为参数应该以引用的形式传入(),因此需要使用std::ref()

    future和promise的作用是在不同线程之间传递数据。使用指针也可以完成数据的传递,但是指针非常危险,因为互斥量不能阻止指针的访问;而且指针的方式传递的数据是固定的,如果更改数据类型,那么还需要更改有关的接口,比较麻烦;promise支持泛型的操作,更加方便编程处理。std::promise的作用就是提供一个不同线程之间的数据同步机制,它可以存储一个某种类型的值,并将其传递给对应的future, 即使这个future不在同一个线程中也可以安全的访问到这个值。
    示例代码:

    //promise的使用,多线程中的函数所使用的参数需要其他线程返回
    //1.子线程使用主线程传入的值
    #include <thread>
    #include <future>
    #include <iostream>
    
    void task(/*std::future<int> i*/std::promise<int>& i)
    {
    	std::this_thread::sleep_for(std::chrono::seconds(1));
    	std::cout << i.get_future().get();
    	//std::cout << i.get() ; // 阻塞,直到 p.set_value() 被调用
    }
    
    int main()
    {
    	//lambda表达式和函数效果一样
    	//auto task = [](std::future<int> i) 
    	//{
    	//	std::this_thread::sleep_for(std::chrono::seconds(3));
    	//	std::cout << i.get() << std::flush; // 阻塞,直到 p.set_value() 被调用
    	//};
    
    	std::promise<int> p;
    	//std::thread t(task, p.get_future());
    	std::thread t(task, std::ref(p));
    
    	p.set_value(5);
    	t.join();  //调用完join后执行task(),因此在这里会阻塞
    
    	return 0;
    }
    
    //2.主线程使用子线程得到的值
    //#include <iostream>
    //#include <future>
    //#include <thread>
    //
    //void task(std::promise<int>& i)
    //{
    //    //dosomething
    //    int value = 8;
    //    i.set_value(value);
    //}
    //
    //int main() 
    //{
    //    std::promise<int> pro;
    //    std::future<int> ret = pro.get_future();
    //
    //    std::thread t(task, std::ref(pro));
    //    t.join();
    //
    //    std::cout << "get value:" << ret.get() << std::endl;
    //
    //    system("pause");
    //    return 0;
    //}
    

    std::packaged_task和std::future的使用

    std::packaged_task的作用就是提供一个不同线程之间的数据同步机制,std::packaged_task本身和线程没有关系,它只是关联了一个std::future的仿函数。需要显式的调用或者传递给std::thread进行异步调用,所以它更灵活(可以选择什么时候开始任务)。

    std::packaged_task 对象内部包含了两个最基本元素

    • 被包装的任务(stored task),任务(task)是一个可调用的对象,如函数指针、成员函数指针或者函数对象

    • 共享状态(shared state),用于保存任务的返回值,可以通过 std::future 对象来达到异步访问共享状态的效果。
      示例代码

    //packaged_task的使用,直接得到多线程调用函数的返回值
    #include <iostream>     // std::cout
    #include <utility>      // std::move
    #include <future>       // std::packaged_task, std::future
    #include <thread>       // std::thread
    
    int fun(int a)
    {
        std::this_thread::sleep_for(std::chrono::seconds(3));
        return 2 * a;
    }
    
    int main()
    {
        std::packaged_task<int(int)> foo(fun);
        std::future<int> ret = foo.get_future();
    
        std::thread t(std::move(foo), 19);
        t.join();  //阻塞,调用fun函数得到返回值
    
        std::cout << ret.get();
    
        return 0;
    }
    

    async、promise、paceaged_task三者的区别与联系

    • 用 std::async 来做简单的事情,例如异步执行一个任务。但是要注意 std::future 析构阻塞的问题。

    • std::packaged_task 能够很轻松的拿到 std::future,选择是否配合 std::thread 进行异步处理。同时没有析构阻塞的问题。

    • std::promise 是三者中最底层的能力,可以用来同步不同线程之间的消息

  • 相关阅读:
    [转]理解I/O Completion Port
    Android OnTouchListener 导致 OnClickListener 接收不到消息 无反应
    linux如何查看所有的用户和组信息?
    本地连接属性:Internet协议版本4(TCP/IPv4)打开闪退解决办法
    解决IE11安装时需要“获取更新”(IE11离线安装)
    plsql中文乱码问题(显示问号)
    npm配置国内镜像资源+淘宝镜像
    Node.js安装及环境配置之Windows篇
    Docker报错 WARNING: IPv4 forwarding is disabled. Networking will not work.
    VirtualBox中CentOS7.2 网络配置(固定IP+联网)
  • 原文地址:https://www.cnblogs.com/mmmmmmmmm/p/14574473.html
Copyright © 2011-2022 走看看