赞
踩
作者:小 琛
欢迎转载,请标明出处
参考文章:
傻月菇凉博主-博客
C++11提出了线程相关的内容,我们可以直接用它来实现线程操作
看一个例子:
#include <iostream> #include <thread> void test() { std::cout << "hello world!" << std::endl; } int main() { //hello函数会在新的线程中执行 std::thread t(test); //join会在调用线程等待std::thread对象相关联的线程结束 t.join(); system("pause"); //msvc return 0; }
函数 | 类别 | 作用 |
---|---|---|
thread() noexcept | 默认构造函数 | 创建一个线程 |
template <class Fn, class… Args>explicit thread(Fn&& fn, Args&&… args) | 初始化构造函数 | 创建一个线程,以args为参数 |
~thread() | 析构函数 | 析构对象 |
一般来说,我们使用thread去创建一个线程,直接调用接口即可,再将希望执行的异步函数传入即可;而对于执行异步函数的参数,可以直接在创建时传入。注意,一定要保证传入的参数和执行异步函数的参数对应,否则会编译错误!
例如下面的例子:
// Compiler: MSVC 19.29.30038.1 // C++ Standard: C++17 #include <iostream> #include <thread> using namespace std; void countnumber(int id, unsigned int n) { for (unsigned int i = 1; i <= n; i++); cout << "Thread " << id << " finished!" << endl; } int main() { thread th[10]; for (int i = 0; i < 10; i++) th[i] = thread(countnumber, i, 100000000); for (int i = 0; i < 10; i++) th[i].join(); return 0; }
传入的函数支持函数指针、lambda表达式
但我们执行下面的代码,会发生编译错误:
#include<iostream> #include<thread> using namespace std; void thread1() { for(int i=0;i<20;++i) cout << "thread1..." << endl; } void thread2() { for (int i = 0; i<20; ++i) cout << "thread2..." << endl; } int main(int argc, char* argv[]) { thread th1(thread1); //实例化一个线程对象th1,该线程开始执行 thread th2(thread2); cout << "main..." << endl; return 0; }
原因:main函数在创建线程后,继续执行,并retrun 0即结束,但异步的线程并没有结束而引发异常。
熟悉线程相关知识的同学都知道,在创建线程后,要对它“负责到底”
涉及线程,线程安全是一定提出的话题,关于线程安全不在该文章赘述。C++11提出了mutex相关内容,使用要包含头文件《mutex》
举个例子:
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex m; int cnt = 10; void thread1() { while (cnt > 5){ m.lock(); if (cnt > 0) { --cnt; cout << cnt << endl; } m.unlock(); } } void thread2() { while (cnt > 0) { m.lock(); if (cnt > 0) { cnt -= 10; cout << cnt << endl; } m.unlock(); } } int main(int argc, char* argv[]) { thread th1(thread1); //实例化一个线程对象th1,该线程开始执行 thread th2(thread2); th1.join(); th2.join(); cout << "main..." << endl; return 0; }
提起线程会引发线程安全,而提出锁则一定引发死锁相关内容,为了避免该问题发生,C++提出了std::lock_guard,利用RAII思想来规避
使用方法很简单,在需要上锁的地方,将声明的mutex锁交给std::lock_guard管理,它的构造会自动帮助你上锁,而在代码执行完后即对象析构时,会自动解锁,避免了死锁的出现,在实际开发中,应多用这种方式。
看个例子:
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex m; int cnt = 10; void thread1() { while (cnt > 5){ lock_guard<mutex> lockGuard(m); if (cnt > 0) { --cnt; cout << cnt << endl; } } } void thread2() { while (cnt > 0) { lock_guard<mutex> lockGuard(m); if (cnt > 0) { cnt -= 10; cout << cnt << endl; } } } int main(int argc, char* argv[]) { thread th1(thread1); //实例化一个线程对象th1,该线程开始执行 thread th2(thread2); th1.join(); th2.join(); cout << "main..." << endl; return 0; }
主要为了解决thread相关的以下几点:
std::async可以直接拿到线程执行结果;
std::async可以避免线程创建失败的情况;
std::async可以手动触发;
原型:
template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);
std::async中的第一个参数是启动策略,它控制std::async的异步行为,我们可以用三种不同的启动策略来创建std::async
通常情况下,我们使用get配合异步操作,看下面的伪代码
std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
//从文件获取数据
std::future<std::string> fileData = std::async(std::launch::deferred, fetchDataFromFile, "Data");
//直到调用get函数fetchDataFromFile才开始执行
std::string FileData = fileData.get();
//如果fetchDataFromDB()执行没有完成,get会一直阻塞当前线程
std::string dbData = resultFromDB.get();
区别在于:get()+std::launch::async(异步),调用get时异步开始进行,并且不会影响当前线程;get()+std::launch::deferred,当该异步函数没有执行结束,get会一直阻塞当前线程
std::async和std::thread最明显的不同,就是async有时候并不创建新线程。
- 如果你用std::launch::deferred来调用async会怎么样?
std::launch::deferred延迟调用,并且不创建新线程,延迟到future对象调用 get()或者 wait()
的时候才执行mythread()。 如果没有调用get或者wait,那么这个mythread()不会执行。- std::launch::async:强制这个异步任务在新线程上执行,这 意味着,系统必须要给我创建出新线程来运行mythread()。
- std::launch::async | std::launch::deferred,这里这个 | :意味着调用async的行为可能是
“ 创建新线程并立即执行” 或者 没有创建新线程并且延迟到调用 result.get()才开始执行任务入口函数,两者居其一。- 不带额外参数,只给async函数一个 入口函数名: 默认值应该是std::launch::async |
std::launch::deferred;和c)效果完全一致。
换句话说:系统会自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行。
std::thread创建线程,如果系统资源紧张,创建线程失败,那么整个程序就会报异常崩溃(有脾气)。
std::thread创建线程的方式,如果线程返回值,你想拿到这个值也不容易
std::async创建异步任务。可能创建也可能不创建线程。
std::async调用方法很容易拿到线程入口函数的返回值。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。