目录
一、atomic
1、std::atomic_flag
atomic_flag 一种简单的原子布尔类型,只支持两种操作,test-and-set 和 clear。
std::atomic_flag 只有默认构造函数,拷贝构造函数已被禁用
ATOMIC_FLAG_INIT: 如果某个 std::atomic_flag 对象使用该宏初始化,那么可以保证该 std::atomic_flag 对象在创建时处于 clear 状态。
test_and_set() :检查 std::atomic_flag 标志,如果 std::atomic_flag 之前没有被设置过,则设置 std::atomic_flag 的标志,并返回先前该 std::atomic_flag 对象是否被设置过,如果之前 std::atomic_flag 对象已被设置,则返回 true,否则返回 false。
clear():清除 std::atomic_flag 对象的标志位,即设置 atomic_flag 的值为 false。
由此可以构造一个简单的锁
std::atomic_flag lock = ATOMIC_FLAG_INIT; //clear 状态
while (lock.test_and_set()); //返回false,同时设置atomic_flag的标志,之后其
/* */ //他线程调用此函数都返回true
lock.clear(); //重置为 clear 状态,其他线程开始抢占
2、 std::atomic
std::atomic为C++11封装的原子数据类型。
先上张图:
以上就是支持的数据类型。
原子操作好像是在汇编上实现的,所以速度要比各种锁快上不少(好像)
除此之外,还可以进行std::atomic<T>模板类使对象操作为原子操作。
二、std::thread
#include <iostream>
#include <thread>
using namespace std;
void fun1()
{
cout<<"fun1"<<endl;
}
void fun2(int a)
{
cout<<a<<endl;
cout<<"fun2"<<endl;
}
int main()
{
thread t1(fun1);
thread t2(fun2, 10);
t1.join();
t2.detach();
return 0;
}
在程序运行时,会出现以下的几种情况:
1、主线程先运行结束
2、子线程先运行结束
3、主子线程同时结束
join()函数是一个等待线程完成函数,主线程需要等待子线程运行结束了才可以结束,在实例程序中,t1.join()会等待t1线程退出后才继续往下执行。
detach()是分离线程函数,使用detach()函数会让线程在后台运行,即说明主线程不会等待子线程运行结束才结束。
三、std::condition_variable
std::condition_variable一般与std::unique_lock配合使用
std::condition_variable类的成员函数:
(1)、构造函数:仅支持默认构造函数,拷贝、赋值和移动(move)均是被禁用的;
(2)、wait:当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_*唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知wait()函数也是自动调用std::mutex的lock();
(3)、wait_for:与wait()类似,只是wait_for可以指定一个时间段,在当前线程收到通知或者指定的时间超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_for返回,剩下的步骤和wait类似;
(4)、wait_until:与wait_for类似,只是wait_until可以指定一个时间点,在当前线程收到通知或者指定的时间点超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_until返回,剩下的处理步骤和wait_until类似;
(5)、notify_all: 唤醒所有的wait线程,如果当前没有等待线程,则该函数什么也不做;
(6)、notify_one:唤醒某个wait线程,如果当前没有等待线程,则该函数什么也不做;如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified);
四、右值引用
关于左值与右值:
一个左值表达式代表的是对象本身,而右值表达式代表的是对象的值;变量也是左值。
先看几行代码
int main()
{
int b = 0;
int &a = b;
int &c = 0;
return 0;
}
很明显,无法通过编译。
error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'|
引用是变量的别名,使用常量初始化引用是错误的。
当然,我们可以通过添加const来进行初始化,但不能对其的值进行修改。
右值引用:
int main()
{
const int &c = 0;
c = 2;
cout<<c;
}
可以成功输出2.
底层实现: 先对一个临时变量赋值为0,在将临时变量储存到c中。
class A
{
public:
int a;
};
A fun()
{
return A();
}
int main()
{
int &&c = 0;
c = 2;
A &&a = fun();
return 0;
}
fun()返回的右值本来在表达式语句结束后,其生命也就该终结了(因为是临时变量),而通过右值引用,该右值又重获新生,其生命期将与右值引用类型变量a
的生命期一样,只要a
还活着,该右值临时变量将会一直存活下去。实际上就是给那个临时变量取了个名字。
五、std::function、std::bind
举个栗子:
#include <iostream>
#include <functional>
using namespace std;
class A
{
public:
typedef int(*AddFun)(int, int);
void Add(AddFun fun, int a, int b)
{
int sum = fun(a,b);
cout<<sum<<endl;
}
void Add(function<int(int,int)> fun, int a,int b)
{
int sum = fun(a,b);
cout<<sum<<endl;
}
};
class B
{
public:
int add(int a, int b)
{
return a + b;
}
};
int add(int a, int b)
{
return a + b;
}
int main()
{
A a;
a.Add(add,1,2);
B b;
a.Add(bind(&B::add,b,placeholders::_1,placeholders::_2),1,2);
return 0;
}
std::bind第一个参数为对象函数指针,表示函数相对于类的首地址的偏移量;
testAdd为对象指针;
std::placeholders::_1和std::placeholders::_2为参数占位符,表示std::bind封装的可执行对象可以接受两个参数。
当然,也可以这么用:
void add(int a, int b)
{
cout << a + b;
}
auto fun = std::bind(add, std::placeholders::_1, std::placeholders::_2);
fun1(3, 1);
其中fun的类型就是function<int(int,int)>。
六、Lambda表达式
[capture list] (params list) mutable exception-> return type { function body }
capture list:捕获外部变量列表
params list:形参列表
mutable指示符:用来说用是否可以修改捕获的变量
exception:异常设定
return type:返回类型
function body:函数体
其中[capture list]:
1.[var]表示值传递方式捕捉变量var;
2.[=]表示值传递方式捕捉所有父作用域的变量(包括this);
3.[&var]表示引用传递捕捉变量var;
4.[&]表示引用传递方式捕捉所有父作用域的变量(包括this);
5.[this]表示值传递方式捕捉当前的this指针。
int main()
{
auto fun = [](int a,int b){cout<<a+b;};
fun(1,2);
return 0;
}
/** output: 3 **/