并发:同一时段
并行:同一时刻
每一个进程都必有一个主线程,主线程是用来调用main函数的。
同一电脑进程间通信方式:管道,文件,消息队列,共享内存(速度最快)
不同电脑进程间通信方式:socket
创建thread对象的方法
#include
1.通过函数创建:
void print(){
cout<<"this is print"<<endl
}
thread mythread(print);
2.重载()运算符的类的对象
class myclass{
public :
void operator()(){
cout<<"this is operator"<<end;
}
};
myclass mc;
thread mythread(mc);
或者 thread mythread((myclass()));
用对象创建线程时,线程中的对象是一个拷贝的新的对象与原对象值相同但没有关联,若原对象被析构,新对象依然可以使用。
3.lambda表达式
auto mylambda=[]{
cout<<"this is lambda"<<endl;
}
thread mythread(mylambda);
thread函数:
(1)thread() noexcept;
(2)template <class Fn, class... Args>explicit thread (Fn&& fn, Args&&... args);
(3)thread (const thread&) = delete;
(4)thread (thread&& x) noexcept;
(1). 默认构造函数,创建一个空的 thread 执行对象。
(2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
cout << "Thread " << n << " executing\n";
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
cout << "Thread 2 executing\n";
++n;
}
}
int n = 0;
thread t1; // t1 is not a thread
thread t2(f1, n + 1); // pass by value
thread t3(f2, std::ref(n)); // pass by reference
thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a
join(); 加入阻塞,当一个父线程调用了一个子线程,当子线程join时,父线程会等到子线程执行完毕再执行
detach(); 表示该线程在后台允许,失去和父线程的关系,它们的执行关系随机,但是当主线程执行完毕时,所有线程都会结束运行。 detach和join不能并存
joinable(); 返回一个bool变量,表示当前线程是否能够join和detach,当我们在join或者detach之前应该调用joinable函数判断当前线程是否能够join和detach。
get_id(); 返回int变量,表示该线程的id
void myprint() {
cout << "this is a print1" << endl;
cout << "this is a print2" << endl;
cout << "this is a print3" << endl;
cout << "this is a print4" << endl;
}
int main() {
thread ob(myprint);
if (ob.joinable()) {
ob.join();
//ob.detach;
cout<<"thread id: "<<id<<endl;
}
cout << "is over1" << endl;
cout << "is over2" << endl;
cout << "is over3" << endl;
cout << "is over4" << endl;
system("pause");
return 0;
}
第一幅图是调用join的运行结果,第一幅图和第三幅是调用detach的运行结果,发现第一幅图和第三幅的打印顺序并不一样。
一些陷阱:
void print(consr int &i,char *carr){
cout<<i<<endl;
cout<<carr<<endl;
}
void print(consr int &i,const string &carr){ //修改一下
cout<<i<<endl;
cout<<carr<<endl;
}
int mvar=1;
int &mmvar=mvar;
char mybuf[]="this is a test";
thread mythread(print,mmvar,mybuf);
mythread.detach(); //当使用detach时,mmvar和i的指向同但不是一对象,当mmvar被释放时,i仍然能够使用,但carr和mybuf是一样的,mybuf被释放后,carr就不能用了,这样调用detach时会出现错误。修改之后,变为string的carr和mmvar的地质不一样说明他们不是一个对象,但是因为主线程和子线程的执行顺序不确定,若主线程先执行并释放那么carr的构造仍然是错误的。
改写为:
thread mythread(print,mmvar,string(mybuf)); //string(mubuf)创建了一个临时对象,这是函数就是正确的了。任意类型的对象都可以这样。但是函数参数中仍然不能存在指针。
传递类对象作为函数参数
class A{
private:
int m_i;
public:
A(int i):m_i(i){}
~A(){}
void A_print(int num){
cout<<num<<endl;
}
}
A a(10);
void print(A &c){
cout<<c.m_i<endl;
}
thread mythread(print,std::ref(a));
智能指针作为函数参数
void print(unique_ptr<int> p){
}
unique_ptr<int> q(int int(100));
thread mythread(print,std::move(q))
成员函数指针做参数
thread mythread(&A::A_print,a,15)
多个线程:
vector<thread> mythread;
for (int i = 0; i < 10;i++) {
mythread.push_back(thread(myprint,i));
}
auto it = mythread.begin();
while(it!=mythread.end()) {
it->join();
it++;
}
只读数据时:
vector<int> g_v = { 1,2,3 };
void myprint() {
cout << std::this_thread::get_id()<<g_v[0] << g_v[1] << g_v[2] << endl;
}
vector<thread> mythread;
for (int i = 0; i < 10;i++) {
mythread.push_back(thread(myprint));
}
auto it = mythread.begin();
while(it!=mythread.end()) {
it->join();
it++;
}
共享数据保护:
使用mutex给共享数据加锁
class A {
public:
void getMessage() {
for (int i = 0; i < 10000;i++) {
cout << "收到一个信息 " <<i<< endl;
std::lock(mymutex, mymutex2);
//mymutex.lock();
//mymutex2.lock();
mylist.push_back(i);
mymutex.unlock();
mymutex2.unlock();
}
}
bool outInf(int &command) {
//std::lock_guard<mutex> mylockguard(mymutex); //构造函数阶段lock(),析构函数阶段unlock()
//std::lock() 一次能够锁住2个或者2个以上的互斥量,至少2个锁,一次性锁住所选的互斥量,若有一个互斥量已经被锁,则
//释放掉自己所锁住的互斥量然后等待所选的互斥量都未被加锁
//mymutex.lock();
//mymutex2.lock();
std::lock(mymutex,mymutex2);
//存在std::adopt_lock时,lock_guard不会在构造函数中调用lock()
std::lock_guard<mutex> mylockguard(mymutex,std::adopt_lock);
std::lock_guard<mutex> mylockguard(mymutex2,std::adopt_lock);
if (!mylist.empty()) {
command = mylist.front();
cout << "输出信息 " << command << endl;
mylist.pop_front();
//mymutex.unlock();
//mymutex2.unlock();
return true;
}
else {
//mymutex.unlock();
//mymutex2.unlock();
return false;
}
}
void outMessage() {
int c = 1;
for (int i = 0; i < 10000; i++) {
bool res = outInf(c);
if (res == true) {
cout << "信息为:" << c << endl;
}
else {
cout << "没有信息:" << endl;
}
}
}
private:
list<int> mylist;
mutex mymutex; //互斥量
mutex mymutex2; //互斥量
};
unique_lock比lock_guard灵活
std::adopt_lock():标志位,表示这个互斥量已经被lock了,若互斥量没有被加锁,则不能用这个参数
mutex mymutex;
mymutex.lock()
std::unique_lock <mutex> mylockguard(mymutex, std::adopt_lock);
std::try_to_lock():会尝试用mutex的lock()去锁定这个锁,但如果没有锁定成功,也会立即返回,不会阻塞,
unique_lock <mutex> mylockguard(mymutex, std::try_to_lock);
注意自己不能提前lock,否则可能导致一个互斥量锁2次
std::defer_lock():创建一个没有加锁的mutex;
unique_lock <mutex> mylockguard(mymutex, std::defer_lock);
mylockguard.lock();
unique_lock的成员函数
lock():
unique_lock <mutex> mylockguard(mymutex);
mylockguard.lock();
unlock();
mylockguard.unlock();
try_lock() 失败返回false,成功返回true
mylockguard.try_lock();
release() 返回它管理的互斥量对象指针,并释放所有权,也就是说unique_lock和这个互斥量再也没有关系
std::mutex *ptr=mylockguard.realease();
锁的粒度指锁所锁住的代码的数量
所有权的转移:
unique_lock <mutex> mylockguard2(std::move(mylockguard ));
单例模式:
mutex mymutex;
std::once_flag g_flag;
class SClass {
static void CreatInstance() {
std::chrono::milliseconds dura(10000);
std::this_thread::sleep_for(dura);
m_instance = new SClass();
cout << "creat obj" << endl;
static FreeObj cl;
}
private:
SClass() {}
private:
static SClass *m_instance;
public:
static SClass *getInstance() {
//if (m_instance == NULL) { //双重检查,提高效率,因为只是第一次初始化的代码需要加锁,
// //大多数情况是不需要加锁,若只检查一次,意味着即使对象已经创建,仍然会做加锁操作,
// //其它线程仍会等待,浪费时间
// std::unique_lock<mutex> myguard(mymutex);
// if (m_instance == NULL) {
// m_instance = new SClass();
// static FreeObj cl;
// }
//}
std::call_once(g_flag,CreatInstance); //g_flag相当于一个互斥量,两个线程同时执行到这里时,
//其中一个线程会等到另一个线程执行完
cout << "call once is over"<<endl;
return m_instance;
}
class FreeObj { //内部类,用来回收创建的对象所占的资源
public:
~FreeObj() {
if (m_instance) {
delete SClass::m_instance;
SClass::m_instance = NULL;
}
}
};
void fun() {
cout << "test" << endl;
}
};
SClass * SClass::m_instance = NULL;
void mythread() {
cout << "mythread线程开始" << endl;
SClass *p = SClass::getInstance();
p->fun();
}
//std::call_once(): 有2个参数,第二个参数为一个函数名,保证函数a只能调用一次
//call_once()具备互斥量的能力,效率上比互斥量好
//call_once()需要与一个标记结合使用,std::once_flag,当已经调用了一次后,std::once_flag就被置为已调用
int main() {
//SClass *pa = SClass::getInstance();
thread thread1(mythread);
thread thread2(mythread);
thread1.join();
thread2.join();
return 0;
}
未完待续