thread
Posted finesimpletop
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了thread相关的知识,希望对你有一定的参考价值。
多线程编程
并发编程的目的是:
- 提高资源的利用率
- 提高响应速度
常见资源
- 带宽
- 链接数
- cpu
- 内存
- gpu
同步原语
-
原子操作
- 保证内存读取-修改-写回序列原子执行。
- 原子意味着本地cpu不被中断或内存总线(或缓存)加锁
- 是同步的基础
-
锁
- 原子操作
- 状态
自旋锁(spinlock)
锁 + 自旋
信号量(semaphore)
锁 + 等待队列
互斥量(mutex)
binary 信号量
条件变量(condition variable)
管程(monitor)
高级同步元素,编程语言实现。有(变量 + 操作组成)
内存模型
多核CPU,每个CPU单独的Cache,主内存只有一个。
重排序
- 编译器重排 合并读 合并写 变量推测(省掉分支)中间变量消除 很多优化在多线程下是不安全的。
- cpu重排 指令预取,pipeline调度
- 内存重排 由于缓存存在,写缓存意味着还没结束,并且难以预测何时结束。多线程下不安全。
内存屏障
屏障导致缓存flush和失效
简单分为四类:
- loadload 读屏障 禁止前后读重排
- storestore 写屏障 禁止前后写重排
- storeload 一般屏障 禁止前后读和写重排
- loadstore 少见。linux还有一种数据依赖的屏障
另外锁/原子量的acquire/release操作隐含了内存屏障的作用。
还有sleep thread.start thread.join
happens-before
acquire-release语义
volatile
java 可见性(约束),下一个读一定看见上一次写。
C++ 不要排序,驱动IO,内容无法推测。
相似点都是加入内存屏障(memory barrier),实现缓存flush和缓存失效。
volatile插入内存屏障表,根据机器架构以及前后关系会删除多余的内存屏障。
final
final值初始化以后在不同线程不应改看到不一样的值。
规则:
- 构造函数final赋值 < 对象赋值给引用之间 插入storestore
- 对象赋值 < 对象.final读 插入loadload
仅仅在某些乱序处理器需要特殊处理。
死锁
死锁产生的四个条件:
- 资源互斥
- 保持请求 保有资源再去请求资源
- 不剥夺 资源占有即使超时也不能剥夺
- 循环等待条件
避免死锁办法:
- 获取不同资源锁的顺序一致(都先lock A,再lock B),破坏循环等待条件
- 超时锁,失败释放资源再重试(复杂情况)
- 合并锁,粒度变大,但是太粗,性能差,太细,overhead变大,代码复杂
避免死锁C++
- lock可以锁多个锁,保证顺序,lock_guard(adopt_lock)保证锁的释放
- 如果临界区有用户代码,一般使用锁的超时版本
- hierarchical_mutex(不在标准库中),保证同层级(指调用层级)只能拥有锁一次,并且高级可以再锁低级
诊断死锁
- 通过Jstack或者Jconsole找出JVM中线程的monitor是否存在环
- java中monitor是带有owner信息的,因此很容易通过当前获取的monitor,找出是否存在环
- C++ 也可以通过gdb打印出mutex结构(包含__owner线程字段),但是c++线程中并不保存当前pending的mutex信息,需要人工去看栈信息
以上是关于thread的主要内容,如果未能解决你的问题,请参考以下文章
Python3 多线程编程(threadthreading模块)
ThreadThreading.TimerTask中ShowDialog()方法报错:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式”
ThreadThreading.TimerTask中ShowDialog()方法报错:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式”
ThreadThreading.TimerTask中ShowDialog()方法报错:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式”