? 通过引入线程库的方式,一般是Pthread库的方式引入线程库,注意的是编译器并不知道有线程的存在,编程语言也不知道,仅仅库支持线程的一些操作,这种方式脱离语言不保证语言的安全性
1. 应用程序应该确保一个或者两个以上的进程、线程对任何内存区域的访问收到限制,为了确保当一个线程正在修改内存位置,没有线程可以读取或者修改该位置,可以通过同步线程执行和同步内存的函数(pthread_mutex_lock())达到限制其他线程的操作
2. 禁止竞争使用变量,一个全局变量,A线程同时修改变量,B线程同时读取读取变量,不允许这样,值得注意的是,这种情况的出现不仅仅是相邻字段,在相同内存位置的任何字段都可能出现读取内存位置字段和修改内存位置字段
3. 建议使用Pthread互斥语句防止并发修改共享变量
? 除了多线程程序中可见存在的并发读写、重入。还有编译器和硬件的内存操作重排序也会引入变量竞争
? 例子1:
? 针对于x和y初始化为0,两个线程分别执行一句:
if (x == 1) ++y;
if (y == 1) ++x;
? 因为顺序执行原则,所以结果就是x和y都是0,没啥改变的
? 当编译器可能对代码进行重排序:
++y; if (x != 1) --y;
++x; if (y != 1) --x;
? 那么当编译器转换为这种形式就出现了变量的竞争
例子2:
? 再比如下面c的位域操作
? struct { int a:17; int b:15 } x;
? 在对a进行更改的时候,大致有下面的操作
1. 保存 x的值
进行位操作赋值
将值写回到x
如果是一个顺序执行一个线程执行对a的修改,那没问题,但是如果在步骤1和步骤2之间,另一个线程对b进行修改,那么将会引发一个竞争操作,并且另一个线程对b的更新操作可能失效,尽管他们不在一个位域操作
例子3:
? 对程序的性能优化带来的问题
? 下面的例子:
? 在一个for循环中对于全局变量mt重复更新,使用了pthread_mutex_lock进行锁保护,但是锁是有条件获得的
for (...) {
...
if (mt) pthread_mutex_lock(...);
x = ... x ...
if (mt) pthread_mutex_unlock(...);
}
? 编译器可能因为性能优化和速度把代码进行优化为:
r = x;
for (...) {
...
if (mt) {
x = r; pthread_mutex_lock(...); r = x;
}
r = ... r ...
if (mt) {
x = r; pthread_mutex_unlock(...); r = x;
}
}
x = r;
? 这就有额外引出了竞争,多线程就会有问题
? Pthread 中的 thread mutex lock()和pthread mutex unlock()等都需要一条可以使用原子方式更新内存位置的硬件指令,这个操作隐式地防止在调用周围对内存引用进行硬件重新排序否则还需要单独的硬件屏障指令
1. **以原子方式更新内存位置的硬件指令需要超过100个处理器周期**
2. **硬件指令本身的高成本和构建在其上的pthread原语的更高成本**
性能上 thread mutex lock()> 自旋锁 > volatile > 无锁操作
1. 当竞争出现的时候 c语言模型中没有规定变量可以如何操作,不像java:japan:
2. c语言的规范在多线程场景下没有限制,比如对相邻字段的隐士写入
3. 当出现并发更新,最终结果取决于语言规范给出它们的语义,以及编译器本身进行的实现
4. c编译器不知道线程的存在,所以优化的时候不会考虑线程
5. 比如标准库的引用计数直接使用原子操作
?
原文:https://www.cnblogs.com/zero-waring/p/14327914.html