关于多线程编程想到的

1. 多线程编程实际上是一种"异步"编程。实现异步编程也不一定非得多线程,使用协程也可以进行异步化,而且协程拥有高的执行效率。

2. 在单线程程序中,同一时刻线程只能干一件事,所有的任务都有序地执行,业务逻辑是连续的;

3. 多线程可以利用多核CPU,在物理层面上进行并行计算。协程实现的异步化,本质上是在同一个线程内部执行的长跳转(类似于longjump),在单CPU上切换,无法利用多核实现真异步。

4. 多线程程序,业务逻辑上是割裂的,因为想要将整个业务切割成多个部分放到不同的CPU上处理。这种割裂具体体现就是任务的回调函数,回调函数执行的部分就是被切分出来的剩余业务逻辑。

 5. 线程之间的数据传送应该是单向的,使用任务队列进行解耦。每个线程应该只关心自己的那部分,如果业务逻辑还需要切分,则将剩余业务逻辑继续切割出去,不要试图将执行结果回送,这样会导致线程之间业务逻辑出现耦合。

锁的问题

多线程编程,遇到资源竞争的问题,解决方法是加锁保护。如果锁使用不当,会造成死锁。如何避免死锁:

1. 尽量缩小锁的范围

2. 以相同的顺序加锁

3. 使用锁的超时机制

单生产者单消费者要不要加锁的问题

        如果说写线程在写变量之后的操作逻辑,不依赖读线程是否看到所写的值,且逻辑上允许读线程读到旧值,则可以不加锁,但是读操作和写操作都应该满足原子性。

        问题在于如何保证数据读写的原子性。对于简单数据类型(int, float,bool等),c++ 11提供了相应的原子变量,可以保证其读写的原子性。此外,c++ 11的std::atomic<>类模板不仅是一套特化的类型,其作为一个原发模板也可以对自定义的类型创建对应的原子类型变量。但是自定义的复杂类型作为原子模板参数,有诸多限制,例如不能有虚函数表,必须使用编译器自动合成的拷贝赋值运算符(成员变量也同理),还需要具有按位可比性等,所以通常复杂类型的读写原子性需要借助互斥锁完成。

        因此,大多数情况下,对于共享资源都是需要加锁的。


版权声明:本文为yuanshenqiang原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。