c++11 多线程

class TA
{
public:
    void operator () ()
    {
        cout << "thread start" << endl;
        cout << "thread end" << endl;
    }
};

int main()
{

    TA ta;
    thread mythread(ta);
    mythread.join();
    cout << "Hello World!" << endl;
    return 0;
}
void myprint(const TA &pmybuf)
{
	cout << " my print" << endl;
}

int main()
{
	int mvar = 1;
	std::thread mytobj(myprint, A(mvar));
	...
}

标题:传递临时对象作为线程参数
1.
在创建线程的同时构造临时变量的方法是可行的
a) 若传递int这种简单类型,建议都是值传递,不要用引用,防止节外生枝。
b) 如果传递类对象,避免隐式类型转换。全部都在创建线程这一行就构建出临时对象来,然后再函数参数里,
用引用来接,否则系统还多构造一次对象,浪费。
终极结论:
c)建议不适用detach(),只使用join(),这样就不存在局部变量失效导致线程对内部的非法引用问题

  1. 线程id可以用c++标准库里的函数来获取。std::this_thread::get_id();
  2. 传递类对象,智能指针作为线程参数
    3.1 std::ref()函数
A myobj(10);
std::thread mytobj(myprint, std::ref(myobj));

3.2

void myprint(unique_ptr(int) pzn)
{
}

int main()
{
	unique_ptr<int> myp(new int(100));
	std::thread mytobj(myprint, std:move(myp);
}
  1. 用成员函数指针作为线程参数
class A
{
	void thread_work(int num)
	{
	}
}

int main()
{
	A myobj(10);
	std::thread mytobj(&A::thread_work, &myobj, 15);//第二个参数是引用,才能保证线程里用的是同一个对象
	mytobj.join();
}
class A
{
	void operator()(int num)
	{
	}
}

int main()
{
	A myobj(10);
	std::thread mytobj(std::ref(myobj), 15);
	mytobj.join();
}

多线程操作

int main()
{
	A myobj;
	std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
}

5.1

std::lock_guard<std::mutex> sguard(my_mutex);//原理是:构造函数里执行了lock(),退出的时候析构调用了unlock();

5.2
死锁的一般解决方案:只要两个互斥量上锁顺序一致就不会死锁。

std::lock(my_mutex1, my_mutex2);//有一个锁不住就会都释放

5.3

std::lock(my_mutex1, my_mutex2);//一次锁定多个互斥量
std::lock_guard<std::mutex> sguard(my_mutex1,std::adopt_lock);//这样这一行就不会lock

unique_lock 灵活性比lock_guard好一些,但效率上低一些,内存占用上多一些

std::unique_lock<std::mutex> sguard(my_mutex);

unique_lock的第二个参数
std::adopt_lock:表示这个互斥量已经被lock(必须要把互斥量提前lock,否则会报异常)
标记的效果就是,假设调用方已经拥有了互斥量的所有权(已经被lock()成功了)
用adopt_lock的前提是自己先把mutex先lock上

std::try_to_lock :尝试用mutex()的lock去锁定这个mutex(),但如果没有锁定成功,也会立即返回,并不会阻塞在那里
前提是你自己不能先去lock()

if(sbguard1.own_lock())
{
	//拿到了锁
}

std::defer_lock: 并没有给mutex加锁;初始化了一个没有加锁的mutex。
用defer_lock的前提是你不能自己先lock,否则会报异常。

  1. unique_lock的成员函数
    lock(),unlock(),try_lock(),release()

7.1

std::unique_lock<std::mutex> sguard(my_mutex1,std::defer_lock);
sguard.lock();//示例
//处理共享代码。。。。
sguard.unlock();//
//处理非共享代码。。。。
sguard.lock();
//处理共享代码。。。。
//不需要再unlock().

7.2 try_lock();//尝试给互斥量加锁,如果拿不到锁返回false,如果能拿到锁,返回true。这个函数不阻塞

if(sguard.try_lock() == true)
{
	
}

7.3 release():返回它所管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系

std::unique_lock<std::mutex> sguard(my_mutex1)
std::mutex *ptx = sguard.release();//现在你有责任自己解锁这个my_mutex1;
......
ptx->unlock();//自己unlock()

总结:lock锁住的大妈段越少,执行越快,整个程序运行效率越高。
有人把锁头锁住的diamagnetic多少成为所得粒度,粒度一般用粗细来描述;代码少,粒度细,diamagnetic多。粒度粗。
尽量选择合适粒度的代码进行保护。

7.4 unique_lcok所有权的传递
std::unique_lockstd::mutex sguard(my_mutex1)
sguard拥有my_mutex1的所有权,所有权可以转移,但不能复制。

std::unique_lock<std::mutex> sguard1(my_mutex1)
std::unique_lock<std::mutex> sguard2(std::move(sguard1)//转移所有权,sguard1为空,sguard2与my_mutex1绑定
  1. 设计模式

8.1 单例设计模式
单例:整个项目中,有某个或者某些特殊的类,属于该类的对象,我只能创建一个,多了创建不了

class MyCAS
{
	private:
		MyCAS();
	private:
		static MyCAS *m_instance;
	public:
		static MyCAS *GetInstance()
		{
			if(m_instance = NULL)
			{
				m_instance = new MyCAS();
				static CGarhuishou cl;
			}
			return m_instance;
		}
	class CGarhuishou
	{
		~CGarhuishou()
		{
			if(MyCAS::m_instance)
			{
				delete MyCAS::m_instance;
				MyCAS::m_instance = NULL;
			}
		}
	}
	void func()
	{
		cout << ""<< endl;
	}
}

int main()
{
	MyCAS *p_a = MyCAS::GetInstance();
	p_a->func();
	MyCAS::GetInstance()->func();
}

8.2 单例设计模式共享数据问题分析、解决

static MyCAS *GetInstance()
{
	if(m_instance = NULL)
	{
		m_instance = new MyCAS();
		static CGarhuishou cl;
	}
	return m_instance;
}

8.3 call_once()

std::mutex resource_mutex; 
std::once_flag g_flag;

class MyCAS
{
	static void CreatInstance()
	{
		m_instance = new MyCAS();
		static CGarhuishou cl;
	}
}

std::call_once(g_flag, CreatInstance);//两个线程同时执行到这里,其中一个线程要等另一个线程执行完毕CreatInstance ,第二个线程才决定要不要执行CreatInstance;
std::condition_variable my_cond;
void inMsgRecvQueue()
{
	std::unique_lock<std::mutex> sguard1(my_mutex1);
  my_cond.notify_one();
}
void outMsgRecvQueue()
{
 std::unique_lock<std::mutex> sguard(my_mutex1);

 //如果第二个参数lambda表达式返回值是false,那么wait()将解锁互斥量,并阻塞到本行。堵塞到什么时候呢,阻塞到其他某个线程调用notify_one()成员函数为止.
//如果wait没有第二个参数,my_cond.wait(sguard),那个跟第二个参数返回false的结果一样
//a) wait()恢复后,不断的尝试获取互斥量锁,如果获取不到,那么流程就卡在wait这等着获取,,如果获取到wait()就继续执行
//b) 
	// b.1) 如果wait有第二个参数lambda(),就判断整个lambda表达式,如果返回值是false,那么wait()将解锁互斥量,并阻塞到本行,等待被notify_one()唤醒
	//b.2)如果返回值是true,那么wait()返回,流程继续(此时互斥锁锁着)
  // b.3)如果wait没有第二个参数,那么wait()返回,流程继续	
my_cond.wait(sguard, [this]{
		if(!msgRecvQueue.empty())
			return true;
		return false;
	})
}

9.1 notify_all()
通知所有线程

std::async
//启动一个异步任务:局势创建一个线程并开始执行对应的线程入口函数,它返回一个std::guture对象。
//std::future对象里边就含有线程入口函数所返回的结果,通过get()获取。

int mythread()
{
	return 5;
}

int main()
{
	std::future<int> result =  std::async<mythread>;//创建一个线程并开始执行,绑定关系,流程并不会卡到这里
	
	cout << result.get() << endl;//卡在这里等待mythread执行完毕,拿到结果。只能调用一次,不能调用多次。
	//result.wait();//等待线程返回,本身并不返回结果
	return 0;
}

10.1
std::packaged_task 打包任务,把任务包装起来
//是个类模板,他的模板参数是各种可调用对象,通过,std::pakcaged_tak来把各种可调用对象包装起来,方便将来作为线程入口函数来调用。

std::packaged_task<int(int)> mypt(mythread);//我们把函数mythread通过packaged_task包装起来
std::thread tl(std::ref(mypt), 1);//线程直接开始执行,第二个参数作为线程入口函数的参数
tl.join();//等待线程执行完毕

std::future<int> result = mypt.get_future();

vector <std::packaged_tak<int(int)> > mytasks;

10.2
std::promise,类模板
//我们能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值取出来用
//通过promis保存一个值,在将来某个时刻我们通过把一个future绑定到整个promise上来得到这个绑定的值

void mythread(std::promise<int> &tmpp)
{
}

int main()
{
	std::promise<int> myprom;//
	std::thread tl(mythread, std::ref(myprom), 100);
	tl.join();

	std::future<int> ful = myprom.get_future();
	auto result = ful>get();
	
}

10.3
//如果能够用最少的东西能够写出一个文丁、高效的多线程程序,更值得赞善。
//为能够读懂高手的代码铺路

std::future<int> result = std::async<mythread>;

std::future_status status = result.wait_for(std::chrono::second(6));//  等待6s

if ( status = std::future_status::deferre) // 延迟  std::future_status::timeout //超时状态  std::future_status::ready //ready状态

11.1
std::shared_future 也是个类模板,get(0函数复制数据

std::shared_future<int> result_s(std::move(result));
  1. 原子操作st::atomic

12.1 原子操作概念引出范围
互斥量::多线程编程中保护共享数据

原子操作:理解成一种不需要用到互斥量加锁(无锁)技术的多线程并发编程方式
//或者是在多线程中不会被打断的 程序执行片段; 原子操作效率上比互斥量更胜一筹
//互斥量的加锁一般是针对一个代码段,而原子操作针对的一般是一个变量,而不是一个代码段
//原子操作一般都是指”不可分割的操作“,要么完成,要么不完成。
//一般用于计数或者统计(累计或接收的数据包)

std::atomic<int> g_mycount = 0;

12.2
std::async 深入谈
参数: std::launch::defered[延迟调用],std::launch::async[强制创建一个线程]

std::async()我们一般不叫创建线程,我们一般叫它创建一个异步任务
std::async和std::thread最明显的不同就是async有时候并不创建新线程

12.3
std::async 和std::thread的区别
a)std::thread :创建线程,如果系统资源紧张,创建线程失败,那么整个程序就会报异常崩溃。
b)std::async 一般不会报异常崩溃,因为系统资源紧张导致无法创建线程的时候,不加额外参数的async的调用就不会创 //建新线程,而是后续谁调用了result.get()获取结果,那么异步让你无就运行在这条get()语句所在的线程上

  1. windows临界区

自动析构技术
std::lock_guardstd::mutex sbguard(my_mutex);

  1. recursive_mutex 递归的互斥量
    recursive_mutex递归的独占互斥量:允许同一个线程,同一个互斥量多次被lock,效率上比mutex低。

  2. 带超时的互斥量std::timed_mutex 和 std::recurisive_timed_mutex
    std::timed_mutex 带超时功能的独占互斥量
    std::recurisive_timed_mutex带超时功能的递归独占互斥量

//try_lock_for();//等待一段时间,超时未拿到锁,继续执行
//try_lock_until ;//

17.线程池

  1. 线程创建数量
    a) 2000个线程基本就是极限
    b) 线程数量尽量不要超过500个,能控制在200个以内
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值