c++11 多线程学习记录

多线程相比于多进程而言具有更小的创建,维护开销,线程之间通过共享进程的全局变量和信号通信。
但是是多线程不适用于多主机的情况。

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;该操作不能保证原子性
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值