多线程相比于多进程而言具有更小的创建,维护开销,线程之间通过共享进程的全局变量和信号通信。
但是是多线程不适用于多主机的情况。
c++11创建线程
c++11创建线程的方式十分简单,只需一句 代码 :thread th(func*);
括号里面可以是任何可调用对象{普通函数,类成员函数,仿函数,Lambda表达式}
举个例子:
thread th([](int i){
cout<<"lambda"<<i<<endl;
});
class A{
public:
void func(int i){
cout<<"类成员函数"<<i<<endl;
}
};
...
A a;
thread th(&A::func,&a);
通过类thread构造的线程对象,有两个常用的方法:
th.join()---->阻塞当前线程,直到th线程结束。
th.detach()---->将当前线程与子线程th分离,*其实和不调用任何方法是一样的只不过是如果主线程退出了,子线程还没有结束没有异常而已。*主线程一旦退出,进程也就结束了,所有的子线程都会被强制结束。
th.joinable()---->判断是否可以等待子线程,如果 子线程在之前已经 detach了,则返回false,即 不能再join
类thread的传参默认是值传递,想要引用传递的话,需要加上ref.即 thread th(func,ref(arg))
多线程编程时,经常用到的两个方法:
this_thread::sleep_for(100ms) 让当前线程睡100毫秒
this_thread::get_id() 获得当前线程的Id号
多线程编程对共享数据的保护问题:
多个线程对共享数据同时读没有问题,但是同时写或者同时读写都是用问题的,会导致数据的混乱。
所以对共享数据的读写操作需要加锁处理。
mutex.lock() 上锁,如果别的线程正拿着锁,则该线程阻塞在这里一直尝试拿锁。
//中间是对共享数据的处理。
mutex.unlock() 释放锁
考虑到互斥锁需要配对使用,所以可以像智能指针 那样,c++11封装了两个对象 来使用:
封装成对象后,就实现了RAII机制(资源创建即初始化(什么鬼名字)),这个东西就是使用局部变量来管理资源,因为局部变量在离开作用域后会自动调用析构函数回收资源,避免了资源的泄露。
{
mutex my_mutex;
lock_guard<mutex> myguard(my_mutex)//该构造函数 内部会执行lock函数上锁。
}
//在大括号外部,即离开了作用域,会自动解锁。
unique_lock相比于lock_guard更加的灵活,在构造函数中可以添加try_to_lock标记尝试获取锁。也可以调用unlock函数提前解锁。
{
mutex my_mutex;
unique_lock<mutex> myuk(my_mutex,try_to_lock)
if(myuk.owns_lock()){
上锁成功。
....
myuk.unlock()//提前释放锁
}
else{
上锁失败,但可以在这里处理一些和共享数据无关的代码。而不是一直在那里尝试获取锁。
}
}
编写一个c++的单例模式。
单例模式,是只能创建一个对象的类。考虑到可能有多个线程同时执行GetInstance函数,就用可能创建出多个对象,所以需要将方法编程互斥的 。但是这样每次执行该函数都会加一次锁,导致效率不高。使用双重检查可以提高效率。
mutex mymutex;
class MyCas{
private:
static MyCas* my_instance;
Mycas(){}
public:
MyCas* GetInstance(){
if(my_instance==nullptr){
unique_lock<mutex> myMutex(mymutex);
if(my_instance==nullptr){
my_instance=new MyCas();
}
}
return my_instance;
}
};
//类的静态成员需要在类外初始化。
MyCas* Mycas::my_instance=nullptr;
提一个函数:call_once()所在头文件是mutex。它可以保证函数只被执行一次。
void Init(int i){
cout<<" init "<<i<<endl;
}
//用在那些初始化函数只被初始化一次的场景。
//call_once,
void InitOnce(int i){
static std::once_flag flag;
std::call_once(flag,Init,i);
}
....
for(int i=0;i<3;i++){
thread th(InitOnce,i);
th.join();
}
条件变量:
condition_variable my_cond;
用法示例:
当wait()函数里的条件不满足 时,该线程会释放掉sbguard锁,阻塞在这里释放掉cpu。等待其它线程唤醒,方法:my_cond.notify_one()或者my_cond.notify_all().
unique_lock<mutex> sbguard(MyMutex);
my_cond.wait(sbguard,[this]{
if(!msg.empty())return true;
return false;
});
async()方法
:创建一个异步任务,该方法可以有两种实现方式
future<int> result = async(myThread);//默认会去创建一个子线程执行该方法,并将返回值封装成future类型。
//如果创建线程失败,则按launch::deferred。
future<int> result = async(launch::deferred,myThread);//该方法并不会创建子线程,而且并没有执行该方法,
//而是等到get()方法才执行,如果没有调用result.get(),则该方法永远不会执行。
cout<<result.get()<<endl;
packaged_task<>类模板
用来封装各种可调用对象。
//myThread()的类型是int()
packaged_task<int()> Task(myThread);
thread th(ref(Task));
future<int> res = Task.get_future();
cout<<res.get()<<endl;
atomic<>类模板
用来定义各种原子变量。这样的变量的++,+=操作都是原子性的。
atomic<int> cnt(0);
cnt++;
cnt+=1;
//cnt= cnt+1;该操作不能保证原子性