线程创建
方式一:调用函数
#include<thread>void CreateThread()
{int a=100;cout<<"This is Thread: "<<a<<endl;
}int main()
{thread Threadone(CreateThread);//join是一种阻塞的方式,需要子线程处理完毕之后,才切换回主线程继续执行Threadone.join();//or detach是一种分离处理,归系统来管理Threadone.detach();}
方式二:使用类对象,需要operator( )( )才能成为可调用对象
#include<thread>
#include <iostream>
#include<mutex>
using namespace std;class TA
{
public:void operator()()//不能带参数{cout << "Thread to start" << endl;}
};int main()
{mutex tex;TA ta;thread Mythread(ta);Mythread.join();cout << "主线程执行" << endl;return 0;
}
方法三:类成员函数作为线程入口
#include <iostream>
#include <thread>
using namespace std;class A
{
public:void prin() {};
};int main() {A aa;thread myobj(&A::prin, aa);if (myobj.joinable()) {myobj.detach();}return 0;
}
使用类成员函数作为线程入口封装Thread库存
#pragma once#include<string>
#include<iostream>
#include<thread>class BaseThread
{
public:void Start();void Wait();void Stop();bool is_exit();private:virtual void Run() = 0;private:std::thread thread_;bool is_exit_ = false;};class TestXThread : public BaseThread
{
public:void Run()override;
};
#include "BaseThread.h"void BaseThread::Start()
{thread_ = std::thread(&BaseThread::Run, this);
}void BaseThread::Wait()
{if (thread_.joinable()){thread_.join();}
}void BaseThread::Stop()
{is_exit_ = true;Wait();
}bool BaseThread::is_exit()
{return is_exit_;
}void TestXThread::Run()
{std::cout << "TestXThead Main begin" << std::endl;while (!is_exit()){std::cout << "." << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(1));}std::cout << "TestXThead Main end" << std::endl;
}
#include<iostream>
#include<cstdio>
#include<cmath>
#include"BaseThread.h"
using namespace std;int main()
{TestXThread testth;testth.Start();std::this_thread::sleep_for(std::chrono::seconds(1));testth.Stop();testth.Wait();system("pause");return 0;
}
注意
1.joinable()可以用来查看是否处于join或者detach状态,如果是返回true,否则返回false。
全局函数作为线程入口分析参数传递内存操作
1.线程中所有传递的参数是基础类型,所有参数都会被复制一份。
2.线程中传递的是对象,对象会被复制拷贝,调用了拷贝构造函数。
detach大坑
#include <iostream>
#include <memory>
#include <thread>
using namespace std;
void myprint(const int &i, char *pmybuf)
{cout << i << endl; cout << pmybuf << endl;return;
}int main() {int mavar = 1;int &mvary = mavar;char mybuf[] = "hello";thread myobj(myprint, mavar, mybuf);if (myobj.joinable()) {myobj.detach();}return 0;
}
以上代码存在的问题:
1.mybuf在主线程结束后会被销毁,导致线程中使用出问题。解决问题:
#include <iostream>
#include <thread>
using namespace std;
void myprint(const int &i, const string &pmybuf)
{cout << i << endl; cout << pmybuf.c_str() << endl;return;
}int main() {int mavar = 1;int &mvary = mavar;char mybuf[] = "hello";//string(mybuf)生成临时对象thread myobj(myprint, mavar, string(mybuf));if (myobj.joinable()) {myobj.detach();}return 0;
}
获取线程ID
std::this_thread::get_id()
总结:
1.在使用thread::detach(类对象)或者(基础类型的时候)只要不对其进行(指针)和(引用)等操作是可以保证线程在主线程结束时,也在后台正常运行的。
2.通过构建(临时l类对象)的方法,其构造是在(主线程)中进行构造的,消除了在子线程构造的情况。
线程函数传递指针和引用
1.在传入指针或者引用的情况下,防止线程访问的空间被提前释放,解决方法:
(一) 传递堆内存中
(二) 使用静态的
(三)参数放到类成员当中
2.在线程中真正想要修改传入参数的,需要使用以下函数
std::ref(要传入的参数)
thread myobj(myprint, std::ref(mavar), string(mybuf));
3.如果传递参数为智能指针,可以使用以下方式,需要使用join,detach会出现内存问题
std::move(要传入的智能指针);
unique_ptr<int>ptr(new int(100));thread myobj(myprint, std::move(ptr), string(mybuf));
创建和等待多个线程
#include <iostream>
#include <thread>
#include <vector>using namespace std;//线程入口函数
void myprint(int inum)
{cout << "myprint thread start , thread id is :"<< inum << endl;return;
}int main() {//创建和等待多个线程vector<thread>mythread;//创建十个线程for (int i = 0; i < 10; i++){mythread.push_back(thread(myprint, i));//创建并开始执行线程}for (auto ider = mythread.begin(); ider != mythread.end(); ider++){ider->join();}cout << "main thread" << endl;return 0;
}
共享数据的保护案例
1.以上程序打印的时候会出现以下问题,即打印不一致
2.创建两个线程,一个队列,其中一个线程负责向队列写入,一个线程负责从队列中取出,以此来保证读写平衡;该设计存在一个问题(在读写操作会产生崩溃问题)
#include <iostream>
#include <thread>
#include <vector>
#include <list>using namespace std;class A
{
public://把收到的消息插入到队列void inmsgRecvQueue(){for (int i = 0; i < 100000; ++i){cout << "msgRecvQueue插入一个元素: " << i << endl;msgRecvQueue.push_back(i);}}void outmsgRecvQueue(){for (int i = 0; i < 100000; ++i){if (!msgRecvQueue.empty()){cout << "msgRecvQueue取出一个元素: " << msgRecvQueue.front() << endl;msgRecvQueue.pop_front();}else{cout << "msgRecvQueue队列为空!!!!! " << endl;}}}private:list<int>msgRecvQueue;
};int main() {A aa;//创建和等待多个线程thread objectB(&A::outmsgRecvQueue,&aa);thread objectA(&A::inmsgRecvQueue,&aa);objectA.join();objectB.join();cout << "main thread" << endl;return 0;
}
互斥量
互斥量的作用是对于上述情况,对多线程读写共享数据进行保护。
1.使用mutex进行互斥加锁保护
#include <iostream>
#include <thread>
#include <mutex>
#include <list>using namespace std;class A
{
public://把收到的消息插入到队列void inmsgRecvQueue(){for (int i = 0; i < 100000; ++i){cout << "msgRecvQueue插入一个元素: " << i << endl;//保护位置加锁mymutex.lock();msgRecvQueue.push_back(i);mymutex.unlock();}}bool outMsgLulQueue(int &value){//加锁操作mymutex.lock();if (!msgRecvQueue.empty()){value = msgRecvQueue.front();msgRecvQueue.pop_front();mymutex.unlock();return true;}mymutex.unlock();return false;}void outmsgRecvQueue(){int nums = 0;for (int i = 0; i < 100000; ++i){if (outMsgLulQueue(nums)){cout << "Queue取出的元素为" << nums << endl;}else{cout << "Queue无元素" << nums << endl;}}}private:list<int>msgRecvQueue;mutex mymutex;
};int main() {A aa;//创建和等待多个线程thread objectB(&A::outmsgRecvQueue,&aa);thread objectA(&A::inmsgRecvQueue,&aa);objectA.join();objectB.join();cout << "main thread" << endl;return 0;
}
2.lock_guard<mutex>sbguard(mutex对象名),在使用此模板时不能再使用lock和unlock,其原理为lock_guard在构造函数中执行了参数的lock,在析构的时候执行了参数的unlock。
#include <iostream>
#include <thread>
#include <mutex>
#include <list>using namespace std;class A
{
public://把收到的消息插入到队列void inmsgRecvQueue(){for (int i = 0; i < 100000; ++i){cout << "msgRecvQueue插入一个元素: " << i << endl;{lock_guard<mutex>sbguard(mymutex);//保护位置加锁/*mymutex.lock();*/msgRecvQueue.push_back(i);/*mymutex.unlock();*/}}return;}bool outMsgLulQueue(int &value){//自旋锁lock_guard<mutex>sbguard(mymutex);/*mymutex.lock();*/if (!msgRecvQueue.empty()){value = msgRecvQueue.front();msgRecvQueue.pop_front();/*mymutex.unlock();*/return true;}/*mymutex.unlock();*/return false;}void outmsgRecvQueue(){int nums = 0;for (int i = 0; i < 100000; ++i){if (outMsgLulQueue(nums)){cout << "Queue取出的元素为" << nums << endl;}else{cout << "Queue无元素" << nums << endl;}}}private:list<int>msgRecvQueue;mutex mymutex;
};int main() {A aa;//创建和等待多个线程thread objectB(&A::outmsgRecvQueue,&aa);thread objectA(&A::inmsgRecvQueue,&aa);objectA.join();objectB.join();cout << "main thread" << endl;return 0;
}
3.死锁(至少有两个互斥量才会产生)
存在两个锁a,b的情况下,两个线程A,B。当线程A的a锁定以后,在尝试b锁的时候,由于上下文切换。切换到B线程执行,B线程执行b锁成功,再去尝试a锁的时候会出现a不能加锁,同样线程A也产生同样的情况(即产生死锁)。
解决死锁的方案:
1.加锁顺序保持一致;
2.使用std::lock(参数一,参数二,--),该函数用来同时锁定多个互斥量,只有同时都锁定了才成功,否则会立即把已经锁住的释放锁。
3.std::adopt_lock是一个结构体,标记着在lock_guard中不再对当前参数执行lock操作,使用此方法的前提是互斥量mutex已经执行了lock
lock_guard<mutex>sbguard(mymutex,std::adopt_lock);
4.std::try_to_lock,会尝试使用mutex的lock去锁定mutex,如果没有锁定成功也会立即返回,不会阻塞,使用此方法之前不能调用lock
unique_lock<mutex>sbguard(mymutex, std::try_to_lock);
5.std::defer_lock ,初始化对mutex不进行lock操作,使用该方法的前提是互斥量mutex不能调用lock
unique_lock<mutex>sbguard(mymutex, std::defer_lock);
6. unique_lock<mutex>的成员函数
unique_lock<mutex>unitex(mymutex, std::defer_lock);//加锁unitex.lock();//解锁unitex.unlock();//尝试加锁,成功返回true,失败返回false,失败时不阻塞unitex.try_lock();//返回它所管理的mutex对象,并释放所有权,也就是说unique_lock和mutex不再关联unitex.release();//转移所有权,通过moveunique_lock<mutex>unitex1(std::move(unitex));//判断是否拿锁成功sbguard.owns_lock();
补充部分:
//线程延迟20000毫秒std::chrono::milliseconds dura(20000);std::this_thread::sleep_for(dura);