原子变量
C++11提供了一个原子类型std::atomic,通过这个原子类型管理的内部变量就可以称之为原子变量,我们可以给原子类型指定bool、char、int、long、指针等类型作为模板参数(不支持浮点类型和复合类型)。
原子变量会把线程对数据的读、修改和存储变成一个原子操作,线程从内存读数据传给CPU,CPU再同步到物理内存 -> 三步不可拆分
互斥锁可以锁任意的数据类型,而原子变量只是针对于整形数据
案例
#include <iostream>
#include <thread>
#include <atomic>
#include <mutex>
using namespace std;#if 1
struct Base
{Base(int n, string s) :age(n), name(s) {}int age;string name;
};class Counter
{
public:void increment(){for (int i = 0; i < 100; ++i){//mtx.lock();number++;cout << "+++ increment thread id: " << this_thread::get_id() << ", number: " << number << endl;//mtx.unlock();this_thread::sleep_for(chrono::milliseconds(100));}}void increment1(){for (int i = 0; i < 100; ++i){//mtx.lock();number++;cout << "*** decrement thread id: " << this_thread::get_id()<< ", number: " << number << endl;//mtx.unlock();this_thread::sleep_for(chrono::milliseconds(50));}}
private://int number = 0;atomic_int number = 0;//atomic<Base*> base;//不能包装这个指针指向的数据块的线程安全,只能保证指针移动的线程安全 >>>> 因此必须要加锁//mutex mtx;
};int main()
{Counter c;thread t1(&Counter::increment, &c);thread t2(&Counter::increment1, &c);t1.join();t2.join();
#if 0atomic<char> c('a');atomic_char cc('b');atomic<int> b;atomic_init(&b, 9);cc = 'd';b.store(88);char ccc = c.exchange('e');Base base(123, "luffy");atomic<Base*> atc_base(&base);cout << "c value: " << c << endl;cout << "ccc value: " << ccc << endl;cout << "b value: " << b.load() << endl;Base* tmp = atc_base.load();cout << "name: " << tmp->name << ", age: " << tmp->age << endl;b = 12;
#endifreturn 0;
}
#endif
线程异步
线程异步在执行的时候多个线程不需要各自等待,各自执行各自的就行了
在一个线程里面获取到另外一个线程执行的结果
线程同步和线程异步
线程同步:A线程需要得到B线程的结果,在B线程工作的时候,A线程此时是阻塞等待的
线程异步:A线程需要得到B线程的结果,在B线程工作的时候,A线程此时是不会等待的
多线程程序中的任务大都是异步的,主线程和子线程分别执行不同的任务
std::future
std::future在主线中得到某个子线程任务函数返回的结果
std::promise
std::promise是一个协助线程赋值的类,它能够将数据和future对象绑定起来,为获取线程函数中的某个值提供便利。
promise的使用
通过promise传递数据的过程一共分为5步:
- 在主线程中创建std::promise对象
- 将这个std::promise对象通过引用的方式传递给子线程的任务函数
- 在子线程任务函数中给std::promise对象赋值
- 在主线程中通过std::promise对象取出绑定的future实例对象
- 通过得到的future对象取出子线程任务函数中返回的值。
std::ref()
在外部主线程中创建的promise对象必须要通过引用的方式传递到子线程的任务函数中
在实例化子线程对象的时候,如果任务函数的参数是引用类型,那么实参一定要放到std::ref()函数中,表示要传递这个实参的引用到任务函数中
案例
有几个线程就要用几个future和几个promise
#include <iostream>
#include<thread>
#include<future>
using namespace std;
//在子线程执行任务的时候传出数据给到主线程//2、将promise对象通过引用方式传入子线程的任务函数
void func(promise<string>& p)
{this_thread::sleep_for(chrono::seconds(3));//3、给promise对象赋值p.set_value("我是你亲妈......");//调用后,主线程的get函数解除阻塞this_thread::sleep_for(chrono::seconds(3));//其他事情
}int main()
{//1、在主线程中创建promise对象promise<string> pro;promise<string> pro1;thread t1(func,ref(pro));//整个匿名函数可以看作为函数的地址 相当于有名函数的函数名thread t2([](promise<string>& p){this_thread::sleep_for(chrono::seconds(3));//3、给promise对象赋值p.set_value("我才是你亲妈......");//调用后,主线程的get函数解除阻塞this_thread::sleep_for(chrono::seconds(3));//其他事情},ref(pro1));//4、在主线程通过promise对象取出绑定的future实例对象future<string> f = pro.get_future();future<string> f1 = pro1.get_future();//5、通过得到的future对象取出子线程任务函数中返回的值string str = f.get();//get阻塞函数string str1 = f1.get();cout << "子线程的数据:" << str << endl;cout << "子线程的数据:" << str1 << endl;t1.join();t2.join();return 0;
}
std::packaged_task
这个类可以将内部包装的函数和future类绑定到一起,以便进行后续的异步调用,它和std::promise有点类似,std::promise内部保存一个共享状态的值,而std::packaged_task保存的是一个函数。
案例
#include <iostream>
#include <thread>
#include <future>
#include <string>
using namespace std;string myFunc()
{this_thread::sleep_for(chrono::seconds(3));return "我是路飞,要成为海贼王。。。";
}using funcPtr = string(*)(string, int);class Base
{
public://重载string operator()(string msg){string str = "operator() function msg: " + msg;return str;}operator funcPtr(){return showMsg;}int getNumber(int num){int number = num + 100;return number;}static string showMsg(string msg, int num){string str = "showMsg() function msg: " + msg + ", " + to_string(num);return str;}
};int main()
{
#if 1// 普通函数packaged_task<string(void)> task1(myFunc);// 匿名函数packaged_task<int(int)> task2([](int arg) {return 100;});// 仿函数Base ba;packaged_task<string(string)> task3(ba);// 将类对象进行转换得到的函数指针Base bb;packaged_task<string(string, int)> task4(bb);// 静态函数packaged_task<string(string, int)> task5(&Base::showMsg);// 非静态函数Base bc;auto obj = bind(&Base::getNumber, &bc, placeholders::_1);packaged_task<int(int)> task6(obj);thread t1(ref(task6), 200);future<int> f = task6.get_future();int num = f.get();cout << "子线程返回值: " << num << endl;t1.join();
#endifreturn 0;
}
std::async
td::async函数比前面提到的std::promise和packaged_task更高级一些,因为通过这函数可以直接启动一个子线程并在这个子线程中执行对应的任务函数,异步任务执行完成返回的结果也是存储到一个future对象中,当需要获取异步任务的结果时,只需要调用future 类的get()方法即可,如果不关注异步任务的结果,只是简单地等待任务完成的话,可以调用future 类的wait()或者wait_for()方法。
案例
#include <iostream>
#include <thread>
#include <future>
#include <string>
using namespace std;string myFunc()
{this_thread::sleep_for(chrono::seconds(3));return "我是路飞,要成为海贼王。。。";
}using funcPtr = string(*)(string, int);
class Base
{
public:string operator()(string msg){string str = "operator() function msg: " + msg;return str;}operator funcPtr(){return showMsg;}int getNumber(int num){int number = num + 100;return number;}static string showMsg(string msg, int num){string str = "showMsg() function msg: " + msg + ", " + to_string(num);return str;}
};int main()
{
#if 1cout << "主线程线程ID: " << this_thread::get_id() << endl;future<string> f = async(launch::async, [](int number) {cout << "子线程线程ID: " << this_thread::get_id() << endl;return string("我是海贼王... ") + to_string(number);}, 100);this_thread::sleep_for(chrono::seconds(10));cout << "子线程的返回值: " << f.get() << endl;
#endif
#if 0future<string> f = async([](int number) {this_thread::sleep_for(chrono::seconds(3));cout << "子线程线程ID: " << this_thread::get_id() << endl;return string("我是海贼王... ") + to_string(number);}, 100);future_status status;do {status = f.wait_for(chrono::seconds(1));if (status == future_status::deferred){cout << "子线程还没有执行..." << endl;f.wait();}else if (status == future_status::ready){cout << "数据就绪了, 子线程返回的数据是: " << f.get() << endl;}else if (status == future_status::timeout){cout << "子线程还在执行, 超时时长用完了, 继续等待..." << endl;}} while (status != future_status::ready);
#endifreturn 0;
}