精华内容
下载资源
问答
  • 多线程什么变量要加锁
    千次阅读
    2017-01-09 21:35:33

    是否要加锁?

    加锁的代价有多大?

    现在有个变量 static int g_delRecordFlag = 0;多个线程同时访问,加了互斥锁,感觉资源消耗的有点多。

    这个变量即使多个线程访问冲突,也没有什么大的问题,很纠结是否加锁。
    询问同事,arm访问int是原子型的,可以不加锁。
    网上查询了下,
    1.int变量读写是否是原子操作,是平台相关的,可以看内核相应平台的atomic.h中对atomic_set()和atomic_read()的定义,如果仅是一条汇编指令或者一个“=”赋值语句,那么对int型读写就是原子的;
    2.2个线程访问某个全局变量,如果读写int型变量是原子操作,就可以不用保护,一读一写(写操作仅限于赋值,如果自加自减等就可能不是原子操作了),最好用volatile声明,防止被编译器优化;

    3.如果多线程读写数据,而读数据的频率又远大于写数据的频率,使用读写锁保护比较好。由于读锁是共享的,相比互斥锁,程序并发性会好很多。

    ---------------------------------------------

    同时网上也有人说C语言的原子操作并不是很安全的,如果编译器加了优化,那么还是有风险的。

    -------------------------------------------

    这里我没有加锁,后续进一步研究

    更多相关内容
  • 一、前言 多个线程访问共享变量,会出现一个线程修改变量值后,其他线程看不到最新...public class 多线程变量不可见性问题 { public static void main(String[] args) { // 1、启动子线程,将线程中的flag值改为tr

    一、前言

    多个线程访问共享变量,会出现一个线程修改变量值后,其他线程看不到最新值的情况,但是当我们对使用变量的地方加锁,就可解决这一问题。

    二、加锁后的代码

    package com.zjl.study.多线程.volatile关键字;
    
    /**
     * Created by zjl 2022/5/29
     **/
    public class 多线程下变量不可见性问题 {
      public static void main(String[] args) {
        // 1、启动子线程,将线程中的flag值改为true
        VolatileThread thread = new VolatileThread();
        thread.start();
        // 2、主线程
        while (true){
          // 加锁
          synchronized (VolatileThread.class){
            if(thread.isFlag()){
              System.out.println("主线程执行,此时flag已经改为true!!!!");
            }
          }
        }
      }
    
    }
    
    class VolatileThread extends Thread{
      private boolean flag = false;
    
      @Override
      public void run() {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        // 线程中修改变量值
        flag = true;
        System.out.println("子线程将flag值变为true");
      }
    
      public boolean isFlag() {
        return flag;
      }
    
      public void setFlag(boolean flag) {
        this.flag = flag;
      }
    }
    

    三、运行结果:我们可以看到主线程已经读取到了flag的值变为了true,执行了打印语句。

    在这里插入图片描述

    三、原因

    某一个线程进入synchronized代码块前后,执行过程如下:
    1、线程获得锁。
    2、清空线程自身的工作内存。
    3、从主内存拷贝共享变量最新的值到自己的工作内存成为副本。
    4、执行代码。
    5、将修改后的副本的值刷新回主内存中。
    6、线程释放锁。

    展开全文
  • 多线程 变量 加锁问题

    千次阅读 2017-07-25 18:33:34
    对于x86来说如果其地址是4字节对齐的,那访问是就原子操作。这也是最常见的情况,注意访问是读或写,不是自增等。...对于多线程编程,很多人概念不清,写代码的时候要么是处处加锁,影响性能不说,还容易莫名
    对于x86来说如果其地址是4字节对齐的,那访问是就原子操作。这也是最常见的情况,注意访问是读或写,不是自增等。
    如果你有个这样的结构体,还禁用了对齐的话那就不是了。
    struct S
    {
    	char a;
    	int b;
    

    };

    http://www.xuebuyuan.com/2214900.html

    对于多线程编程,很多人概念不清,写代码的时候要么是处处加锁,影响性能不说,还容易莫名其妙的死锁,还有人对多线程敬而远之。

    所以学习多线程编程最重要的不是学习API,而是理解什么才是多线程安全的代码

    从例子说起

    #include  < windows.h > #include  < process.h >

    long global1 = 0; volatile long global2 = 0;

    class MyClass { public:     MyClass() : m(0)     {         ++m;     }

        int fun(int v)     {         return m+v; //-----------9     }

        void set(int v)     {         m = v;   //-------------10     }     int m; };

    MyClass global_object; //-------------8 unsigned int __stdcall thread_fun1(void *param) {     static int static2 = 0;     static MyClass static_object; //--------6     int local1 = 0;          ++local1;     //-------1     ++static2;    //-------2     ++global1;    //-------3     ++global2;    //-------4     InterlockedIncrement(&global1); //--------5     local1 = global_object.fun(local1); //----------7     global_object.set(local1); //---------------11     return 0; }

    unsigned int __stdcall thread_fun2(void *param) {     ++global1;    //-------3     ++global2;    //-------4     InterlockedIncrement(&global1); //--------5     global_object.set(1); //-----------11     return 0; }

    int main() {     HANDLE thread1 = (HANDLE)_beginthreadex(0,0,&thread_fun1,0,0,0); //thread 1     HANDLE thread2 = (HANDLE)_beginthreadex(0,0,&thread_fun1,0,0,0); //thread 2     HANDLE thread3 = (HANDLE)_beginthreadex(0,0,&thread_fun2,0,0,0); //thread 3          WaitForSingleObject(thread1,INFINITE);     WaitForSingleObject(thread2,INFINITE);     WaitForSingleObject(thread3,INFINITE);          return 0; }

    1.局部变量局部使用是安全的 为什么?因为每个thread 都有自己的运行堆栈,而局部变量是生存在堆栈中,大家不干扰。 所以代码1 int local1; ++local1; 是安全的

    2.全局原生变量多线程读写是不安全的 全局变量是在堆(heap)中 long global1 = 0; ++global2; ++这个操作其实分为两部,一个是读,另外一个是写  mov         ecx,global  add         ecx,1  mov         global,ecx 所以代码3处是不安全的

    3.函数静态变量多线程读写也是不安全的 道理同2 所以代码2处也是不安全的

    4.volatile能保证全局整形变量是多线程安全的么 不能。 volatile仅仅是告诫compiler不要对这个变量作优化,每次都要从memory取数值,而不是从register 所以代码4也不是安全

    5.InterlockedIncrement保证整型变量自增的原子性 所以代码5是安全的

    6.function static object的初始化是多线程安全的么 不是。 著名的Meyer Singleton其实不是线程安全的 Object & getInstance() {       static Object o;      return o; } 可能会造成多次初始化对象 所以代码6处是不安全的

    7.在32机器上,4字节整形一次assign是原子的 比如 i =10; //thread1 i=4; //thread2 不会导致i的值处于未知状态,要么是10要么是4

    其它的大家自己去体会。

    写好多线程安全的法宝就是封装,使数据有保护的被访问到 安全性: 局部变量>成员变量>全局变量

    http://www.phpfans.net/ask/MTMxODE4OQ.html

    对于“多线程访问同一个变量是否需要加锁”的研究

    对于多线程访问同一变量是否需要加锁的问题,先前大家都讨论过。今天用代码验证了一下之前的猜想:32位CPU与内存的最小交换数据为4字节/次,这也是结构体要对齐4字节的原因。在物理上,CPU对于同一4字节的内存单元,不可能写2个字节的同时,又读了3字节。 测试环境为: XEON 2CPU*2 Windows7 采用50,50,50线程交叉读写,试验代码如下:
    C/C++ code
          
    int g_test; int temp; BOOL g_bRunning; DWORD WINAPI thWriteProc1(LPVOID lParam) { while(g_bRunning) { g_test = 12345678; Sleep( 1); } return 0; } DWORD WINAPI thWriteProc2(LPVOID lParam) { while(g_bRunning) { g_test = 13579246; Sleep( 1); } return 0; } DWORD WINAPI thReadProc(LPVOID lParam) { while(g_bRunning) { temp = g_test; // 读取值 if ( temp != 12345678 && temp != 13579246 ) { g_bRunning = FALSE; CString str; str.Format( " read error!%d ", temp); AfxMessageBox(str); break; } Sleep( 1); } return 0; } void CTestMultiyAccessIntDlg::OnButton1() { g_bRunning = TRUE; for ( int i = 0; i < 50; i++ ) { // 创建50个写线程1 CreateThread( NULL, 0, thWriteProc1, NULL, 0, NULL ); } for ( int i = 0; i < 50; i++ ) { // 创建50个写线程2 CreateThread( NULL, 0, thWriteProc2, NULL, 0, NULL ); } for ( int i = 0; i < 50; i++ ) { // 创建50个读线程 CreateThread( NULL, 0, thReadProc, NULL, 0, NULL ); } }


    测试方法:
    改变g_test的类型,给g_test赋予不同的值(不要超过类型的上限值)

    测试现象:
    当g_test为int,short char时,不存在多线程交叉读写错误的问题
    当g_test为double, float, __int64时,存在多线程交叉读写错误的问题,对于__int64,当赋值小于0xFFFFFFFF时不出错,当大于0xFFFFFFFF时出错
    当g_test为CString时,存在交叉读写错误,有时候程序崩溃
    另:不加Sleep(1)机器卡死过,CPU占用率达到100%,4个核心占用率全满,可以保证运行在多核环境下

    现象分析:
    (1)int short char均为小于4字节的连续内存块,CPU一条指令就可以读写它们的值,CPU不可能同一个时间执行两条指令
    (2)double为8字节,如果写线程先写了4字节,读线程读了8字节,这自然导致数据被破坏
    (3)float也为4字节,我也不是太清楚为什么不行,可能是VC对浮点数的处理比较特殊有关,浮点数具有复杂内存结构
    (4)__int64为8字节,存在和(2)相同的情况,如果__int64小于等于0xFFFFFFFF,相当于只改变了低4字节,因此就没有问题
    (5)CString为类类型,具有复杂结构,显然不行

    结论:
    1.对于int,short,char,BOOL等小于等于4字节的简单数据类型,如果无逻辑上的先后关系,多线程读写可以完全不用加锁
    2.尽管float为4字节,多线程访问时也需要加锁
    3.对于大于4字节的简单类型,比如double,__int64等,多线程读写必须加锁。
    4.对于所有复杂类型,比如类,结构体,容器等类型必须加锁

    尽管对int等类型的多线程读写不需要加锁,但是逻辑上有必要加锁的还是应该加锁
    例如:对于一个多线程访问的全局变量int g_test
    int count = g_test/1024;
    int mod = g_test%1024;
    由于是两条语句,执行完第一条之后,别的线程很可能已经修改了g_test的值,如果希望这两条语句执行时,g_test不发生变化,就必须加锁,以保证两条语句执行的整体性。
    Lock();
    int count = g_test/1024;
    int mod= g_test%1024;
    UnLock();
    如果不加锁,也可以改为先保存到一个临时变量里
    int temp = g_test;
    int count = temp/1024;
    int mod = temp%1024;
    昵称: jackson35296  时间: 2010-12-17 14:22:10
    顶。需要楼主这样用心的人,给大家提供实实在在的帮助。
    我没有细看,但估计对我的总结来说就是,加锁吧......
    昵称: happyparrot  时间: 2010-12-17 14:25:58
    如果我没有弄错的话,float应该也是8字节的。

    谢谢楼主对于这个问题的深入研究,学习了~~~
    昵称: QustDong  时间: 2010-12-17 14:58:44
    ‎‎‎‎‎‎
    昵称: linwengk  时间: 2010-12-17 15:02:07
    恩,不错,呵呵~~~谢谢哦~~~


    0

    +=,-=++,--需要。

    x=value;

    value=x;

    不需要加锁。

    0

    展开全文
  • 真实的场景是大部分都不加锁。但是在关键点上也不加锁的话,就会引发偶现的并发竞态问题。下面我们就在一个真实场景中感受一下这个问题。

    在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。


    这个问题按道理来说是非常基础的知识了——最简单的答案就是要加锁。但在现实的场景中还是让人很纠结的,因为现实中的情况是一个源文件中有成百(甚至上千)个全局变量的(国内大部分公司都是饿汉型的——只关注功能不关注架构和规范这种“阳春白雪”的东西),都加上锁不成卖锁的了?所以,真实的场景是大部分都不加锁。

    但是在关键点上也不加锁的话,就会引发偶现的并发竞态问题。下面我们就在一个真实场景中感受一下这个问题。

    1 问题引入

    先交代一下背景。同一个进程中有一个主线程(以下简称TA)和多个从线程(以下简称TBx),主线程只有一个,从线程可以根据业务动态增加和减少。TA和TBx的维护在不同部门(注意这点很重要,常常跨部门的问题都很难快速解决)。TA和TBx共享一个全局变量global,该全局变量的数值由TA进行维护,TBx原则上只对该变量进行读取。

    问题出现的概率极低,在创建多个从线程,并且在特定业务下才会出现。这对我们定位问题提出了巨大的挑战。所以,第一步就是要能够找到问题触发的条件,稳定复现问题才是解决问题的第一步。我们跨过这步之后才来到了今天问题的主角——global的值偶现异常。

    2 问题分析定位

    按照常理来说global的值维护起来是很简单的,主线程每次收到一个中断(中断周期为500us)就自加1而已。但定位过程中就发现有时候自加1就会失败(保持原值或者其他一个异常值,保持原值的情况居多)。

    这么简单的逻辑(其实没有逻辑。。。)居然还能出错?感觉CPU不受控制了。于是乎各种手段加上——防止乱序执行、内存屏障、原子操作。

    然而,复现概率低了,但是仍然没有解决。。。


    到这里思路有点受限了,走入了死胡同。那接下来就是 跳出来 ——我们的前提是否出现了问题?

    Tips:注意这种没有思路后跳到问题的源头或者出发点进行分析的思路非常重要。一切逻辑推理都需要前提,如果大前提错了,那整个推理的大厦都将倒塌。

    分析的前提——TA和TBx共享一个全局变量global,该全局变量的数值由TA进行维护,TBx原则上只对该变量进行读取。

    对该前提进行怀疑的时候,就开始柳暗花明了。

    我们在代码中搜索了global使用的所有位置,大海捞针般地找到了一处非常怪异也十分可疑的代码。

    它长成这样:

    /* 内部接口 */
    static int get_slot(void)
    {
        ... /*此处省略对global范围的校验*/
        return global;
    }
    /* 对外接口:由其他从线程调用 */
    int slot_get(void)
    {
        ...
        /*始作俑者就在这里;实际上是自己给自己赋值了,而且中间还进行了一系列耗时的操作*/
        global = get_slot(); 
        
        return global;
    }
    

    上述代码确实比较奇怪,可能是历史遗留问题。问题就出在 global = get_slot(); 上,包含该代码的函数会被其他从线程调用,这也是为什么从线程创建的越多,问题越容易暴露的原因。


    3 问题解决

    3.1 常规解决

    3.1.1 加锁

    在所有修改global的地方加上锁即可。但这种方式比较笨重,逻辑本身就只有一处写,所以本可以不用加锁的。

    3.1.2 保证只有一个线程写

    针对业务的特点——只需要主线程修改global的值,其他从线程不需要写,只需要读——可以将get_slot()函数与slot_get()函数合并,去除slot_get()中对global的写操作即可。

    3.2 更优的方式

    人为保证只有主线程写global还是比较难的,有没有语法技巧来保证这一点呢?

    有的。可以使用const关键字配合 “特权指针”的方式达到这样的效果。

    下面给出一个小例子:

    #include <stdio.h>
    
    volatile const int global = 0;
    
    void main(void)
    {
        	/* 使用指针(姑且称之为特权指针)指向const变量地址;注意需要强转,不然会报报警 */
            int *pPrivilege = (int *)&global; 
    
            *pPrivilege = 7;
            printf("The global value is %d .\n", global);
    
            //global = 3; /*如果将改行注释去掉编译会报错,提示该变量不可以修改*/
    }
    

    将global定义为const类型,只有通过指针才能修改,直接修改global编译时会报错。

    # 直接修改global编译时会报错
    main.c: In function ‘main’:
    main.c:12:2: error: assignment of read-only variable ‘global’
      global = 3; 
      ^
    

    4 复盘

    1. 进程内共享全局变量,如果有超过一个线程写该变量,则需要加锁(或者保证原子操作)。
    2. 如果只有一个线程写该变量,其他线程均为读,可以不加锁。为了保证只有一个线程写,可以使用const+特权指针的方式。

    恭喜你又坚持看完了一篇博客,又进步了一点点!如果感觉还不错就点个赞再走吧,你的点赞和关注将是我持续输出的哒哒哒动力~~

    展开全文
  • 多线程共享全局变量加锁机制

    千次阅读 2018-08-28 11:14:58
    import threading VALUE = 0 ... #引用全局变量,并保证全局变量不被清零,使用global global VALUE for x in range(1000): VALUE += 1 print('value:%d' % VALUE) def main(): for x in ...
  • 多线程实现方法(加锁加锁

    千次阅读 2020-10-28 18:14:40
    多线程应用程序中,当多个线程共享相同的内存时,如同时访问一个变量时,需要确保每个线程看到一致的数据视图,即保证所有线程对数据的修改是一致的。 一个典型的例子是,在一个多线程程序中,两个及以上个线程对...
  • 多线程编程中,如果多个线程都需要对同一个全局变量进行赋值或者读取操作,那么在每次赋值或读取时,为了确保线程安全,都必须进行加锁和解锁的操作,这样会在编程时带来很多麻烦。特别是线程多、全局变量多的...
  • 主要介绍了C++多线程中的锁和条件变量使用,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
  • 多线程 加锁

    2021-09-24 10:46:27
    多线程来说,资源是共享的,基本上不存在不允许访问的情况,但是,共享的资源在某一时间点只能有一个线程占用,所以需要给资源加锁。 线程的锁的种类 互斥锁 读写锁 条件变量 自旋锁 信号灯 互斥锁 互斥锁...
  • NULL 博文链接:https://toknowme.iteye.com/blog/2212529
  • 共享变量:当线程访问同一个变量的时候。会产生共享变量的问题。 例子: import threading sum = 0 loopSum = 1000000 def myAdd(): global sum, loopSum for i in range(1, loopSum): sum += 1 def myMinu()...
  • 1.Qt下,多线程使用互斥锁安全访问同一全局变量;2.源码中定义了ThreadA和ThreadB,定义变量后,依次调用函数start()来启动重写的run()函数
  • 多线程请求给静态变量加锁

    千次阅读 2019-12-25 18:36:08
    今天在写一个当每次请求过来定义的一个静态变量累加的时候遇到很多坑,发现自己对多线程和锁的理解和使用一窍不通,对Java一些设计模式真的是一知半解!!心生惭愧,感谢部门大佬耐心的讲解和帮助!让我也明白写程序...
  • python多进程和多线程是大家会重点了解的部分,因为很多工作如果并没有前后相互依赖关系的话其实顺序并不是非常的重要,采用顺序执行的话就必定会造成无谓的等待,任凭cpu和内存白白浪费,这是我们不想看到的。...
  • 一、什么时候应该使用多线程? 今天看到一个问题,突然有感而发,想聊下这个话题。 不知道大家有没有想过这个问题,就是什么时候我该使用多线程呢?使用多线程就一定会提升系统性能吗? 1、其实是否应该使用多线程在...
  • 一个线程只读并没有改变变量的值并不会有两个线程同时写一个变量产生竞态,所以不用加锁,但是工作中长者给我指导都是多线程必须加锁,所以我也没有深究这个问题,从来没有想过为什么。 过了一段时间后,了解到原子...
  • 当一个进程的线程需要访问同一个变量的时候,就产生了共享变量的问题。可以通过加锁或者信号灯的方式,解决此问题。 解决互斥 - 方法1:加锁 锁LOCK: 通常对互斥资源进行加锁,在加锁之后,可以对互斥资源...
  • 如果所有线程都只读取该变量的话不必加锁,因为仅读取不存在破坏数据的风险,如果有线程写该变量的话不管读取还是写入都要加锁的。 转载于:https://www.cnblogs.com/panxuejun/p/8151295.html...
  • 多线程加锁原因

    2016-04-22 15:11:11
    多线程编程加锁主要为了防止多个线程在同一时间访问同一资源导致的数据错误。 这句话有些笼统,通过书上介绍的例子,我觉得应该说是为了防止多个线程在同一时间访问同一资源导致访问的不是期望的数据。 比如: ...
  • 如果只是读操作,没有写操作,则可以不用加锁,此种情形下,建议变量加上final关键字;...如果有写操作,且写操作依赖变量的当前值(如:i++),则getXXX和写操作方法都通过synchronized加锁。 转载于:https...
  • 多线程的几种加锁方式详解

    万次阅读 2016-07-17 09:47:19
    NSLock是Cocoa提供给我们最基本的锁对象,这也是我们经常使用的,除lock和unlock外,NSLock还提供了tryLock和lockBeforeDate:两个方法,前一个方法会尝试加锁,如果锁不可用(已经被锁住),并不会阻塞线程,直接...
  • 谈谈多线程下为何需要锁

    万次阅读 多人点赞 2020-08-20 20:11:36
    首先先看看不加锁的情况下多线程共享访问临界资源会发生什么. 代码很简单,开启10个线程,各加上100000,理想结果应该是10 * 100000,好的,来试试吧 ```c #include <stdio.h> #include <pthread.h> #...
  • Java多线程加锁例子

    2021-09-15 01:09:41
    Java多线程加锁例子一、错误示例二、正确示例 一、错误示例 static int a = 0; //多线程同时操作 val a @Test public void test4() throws InterruptedException { Runnable runnable = () -> { ...
  • 多线程访问同一变量是否需要加锁

    千次阅读 2015-09-08 21:49:06
    对于多线程访问同一变量是否需要加锁的问题,先前大家都讨论过。今天用代码验证了一下之前的猜想:32位CPU与内存的最小交换数据为4字节/次,这也是结构体对齐4字节的原因。在物理上,CPU对于同一4字节的内存单元,...
  • 多线程对同一全局变量的读写可能会导致问题。 写操作根据硬件设计不同,可能有三种方式。 (1)写穿过。即直接写内存。 (2)写cache,等cache攒够了之后统一写内存。 (3)写cache同时写内存。 从性能上讲,...
  • C++多线程加锁详解(易于理解)

    千次阅读 2022-03-24 10:39:14
    参考:C++多线程基础教程 c++11多线程之使用std::try_to_lock摸鱼 #include mutex m; // 实例化对象m m.lock(); // 不推荐这么做,如果忘记unlock会锁死。 m.unlock(); // 使用lock_guard or unique_lock避免该问题...
  • 互斥锁是用来防止线程同时访问共享资源对象的机制,在同一时间只有一个线程可以拥有一个特定的锁对象,其他线程如果尝试获取锁会阻塞直到锁资源被释放或直接返回失败。 3、思路 1 全局变量的时候初始化一个全局...
  • 基础数据类型多线程是否需要加锁

    千次阅读 2020-04-23 14:51:37
    参考: ... 说明: (1)intshortchar均为小于4字节的连续内存块,CPU一条指令就...(2)double为8字节,如果写线程先写了4字节,读线程读了8字节,这自然导致数据被破坏; (3)float也为4字节,我也不是太清楚为什么不行...
  • 线程的背景知识、创建线程、 交替获得CPU时间片、多线程抢夺同一个变量、线程 加锁、解锁机制
  • java 多线程加锁

    千次阅读 2020-06-09 16:03:32
    java 加锁方法: 1:直接加 synchronized 关键字 2:使用lock private java.util.concurrent.locks.Lock lock = new ReentrantLock(); private void fun(){ lock.lock(); try{ 执行语句...... } ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 148,171
精华内容 59,268
关键字:

多线程什么变量要加锁