精华内容
下载资源
问答
  • 多线程访问同一变量是否需要加锁

    千次阅读 2015-09-08 21:49:06
    对于多线程访问同一变量是否需要加锁的问题,先前大家都讨论过。今天用代码验证了一下之前的猜想:32位CPU与内存的最小交换数据为4字节/次,这也是结构体要对齐4字节的原因。在物理上,CPU对于同一4字节的内存单元,...
    对于多线程访问同一变量是否需要加锁的问题,先前大家都讨论过。今天用代码验证了一下之前的猜想:32位CPU与内存的最小交换数据为4字节/次,这也是结构体要对齐4字节的原因。在物理上,CPU对于同一4字节的内存单元,不可能写2个字节的同时,又读了3字节。
    

    测试环境为:

    XEON 2CPU*2
    Windows7

    采用50,50,50线程交叉读写,试验代码如下:
    C/C++ code
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    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;
    展开全文
  • 多线程知识简介 同一进程中可以包含多个线程,由于进程中的多个线程可以共享进程中的资源,所以使同一进程中的多个线程之间通信相对比较简单。 当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前...

    多线程知识简介

    同一进程中可以包含多个线程,由于进程中的多个线程可以共享进程中的资源,所以使同一进程中的多个线程之间通信相对比较简单。

    当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前加上volatile声明,来告诉编译器这个全局变量是“易变”(更直接的讲是“直接存取原始内存地址”,更明确的说是不要编辑器去读缓存中的数据,而是直接从内存中获取变量的值)的,让编译器不要对这个变量进行优化。

    一个char类型的变量多线程访问需要加锁互斥;只要是多线程涉嫌冲突访问,就需要同步控制。

    使用多线程相对于多进程来说有很多优点:

    • ① 无需跨进程边界;
    • ② 程序逻辑和控制方式简单;
    • ③ 所有线程可以直接共享内存和变量等;
    • ④ 线程方式消耗的总资源比进程方式好;
       

    多线程通信的方法主要有以下三种: 

    1.全局变量

    由于同一进程下的线程之间共享数据空间。当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前加上volatile声明,以防编译器对此变量进行优化。

    2.Message消息机制
    常用的Message通信的接口主要有两个:PostMessage和PostThreadMessage,
    PostMessage为线程向主窗口发送消息。而PostThreadMessage是任意两个线程之间的通信接口。

    PostMessage() 
    函数原型:
        B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);

    参数:
        hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:
        HWND.BROADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口
    和弹出式窗口。消息不被寄送到子窗口。
        NULL:此函数的操作和调用参数dwThread设置为当前线程的标识符PostThreadMessage函数一样。
        Msg:指定被寄送的消息。
        wParam:指定附加的消息特定的信息。
        IParam:指定附加的消息特定的信息。
        返回值:如果函数调用成功,返回非零值:如果函数调用失败,返回值是零。
    MS还提供了SendMessage方法进行消息间通讯,SendMessage(),他和PostMessage的区别是:

    SendMessage是同步的,而PostMessage是异步的。SendMessage必须等发送的消息执行之后,才返回。
    PostThreadMessage()

    PostThreadMessage方法可以将消息发送到指定线程。
    函数原型:BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam, LPARAM lParam);

    参数除了ThreadId之外,基本和PostMessage相同。
    目标线程通过GetMessage()方法来接受消息。

    注:使用这个方法时,目标线程必须已经有自己的消息队列。否则会返回ERROR_INVALID_THREAD_ID错误。可以用
    PeekMessage()给线程创建消息队列。


    3.CEvent对象

    CEvent为MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步,这个主要是实现线程直接同步的一种方法。


    参考文档:

    http://blog.csdn.net/lanmoshui963/article/details/2176376
    展开全文
  • 情况首先要肯定的是ThreadLocal和局部变量...但在多线程中常常发现这个 变量的增减 会出现错乱 并不是预期中的结果显示。例如:package test.autorun; public class ShareVar { private static int nCount=0; pub



    情况

    首先要肯定的是ThreadLocal和局部变量是线程安全的,静态和实例变量都是不安全的。


    我们常常在系统中会用一些 静态变量 作为 共同的状态标记。


    但在多线程中常常发现这个 变量的增减 会出现错乱  并不是预期中的结果显示。


    例如:

    package test.autorun;
    
    
    public class ShareVar {
    	private static int nCount=0;
    	
    	public  int getnCount() {
    		return nCount;
    	}
    	public  void setnCount(int nCount) {
    		ShareVar.nCount = nCount;
    	}
    	
    
    }
    





    原因

    内存机制中的  "副本"概念 

    多个线程访问一个成员变量时  每个线程都会得到一个该变量的副本  在自己的线程的栈中保存、计算 以提高速度。  但是这样就会有同步的问题了。   当一个线程修改了自己栈内副本的值  还没有立即将同步到主存中, 其他线程再来获取主存中的该变量时  就会得到过期数据。 




    解决办法

    为了解决这种问题 可以使用synchronized对该变量的操作同步 , 或使用volatile关键字声明该变量为易变对象  这样的话 每个线程就不会创建副本到自己的栈中  而是直接操作主存。


    volatile

    在对象/变量前加上 volatile 。 Volatile修饰的 成员变量 在每次被 线程 访问时,都强迫从 共享内存 中重读该成员变量的值。而且,当 成员变量 发生变化时,强迫线程将变化值回写到 共享内存 。这样在任何时刻,两个不同的线程总是看到某个 成员变量 的同一个值。 Java语言 规范中指出:为了获得最佳速度,允许线程保存共享 成员变量 的私有拷贝,而且只当线程进入或者离开 同步代码块 时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享 成员变量 的变化。而volatile 关键字 就是提示JVM:对于这个 成员变量 不能保存它的私有拷贝,而应直接与共享成员变量交互。使用建议:在两个或者更多的线程访问的 成员变量 上使用volatile。当要访问的 变量 已在synchronized代码块中,或者为 常量 时,不必使用。由于使用volatile屏蔽掉了JVM中必要的 代码优化 ,所以在效率上比较低,因此一定在必要时才使用此 关键字 。

    package test.autorun;
    
    
    public class ShareVar {
    	private volatile static int nCount=0;
    	
    	public  int getnCount() {
    		return nCount;
    	}
    	public  void setnCount(int nCount) {
    		ShareVar.nCount = nCount;
    	}
    	
    
    }
    




     synchronized

    将对象/变量加上锁 synchronized 修饰。在线程中,使用同步方法或者同步块。

    package test.autorun;
    
    
    public class ShareVar {
    	private  static int nCount=0;
    	
    	public  int getnCount() {
    		return nCount;
    	}
    	public  synchronized void setnCount(int nCount) {
    		ShareVar.nCount = nCount;
    	}
    	
    
    }
    


    TimerTask

    使用带有线程安全的线程。如:继承 TimerTask 类实现线程,用 Timer.schedule 启动线程   。





    选择

    但synchronized同步方法和块的话 损耗资源和性能是较大的,它会在该方法和块中阻塞变成类似于单线程来运作,所有优先考虑volatile






      

    展开全文
  • 在Java中,如果启动线程对同一个对象或者变量时候,在没有安全保护前提下有可能会抛出并异常 java.util.ConcurrentModificationException 当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常(并发...

    在Java中,如果启动多个线程对同一个对象或者变量时候,在没有安全保护前提下有可能会抛出并异常

    java.util.ConcurrentModificationException

    当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常(并发异常)

    解决方法:

    • 在对象/变量前加上volatile。Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而volatile关键字就是提示JVM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。由于使用volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字
    • 将对象/变量加上锁synchronized修饰。在线程中,使用同步方法或者同步块。
    • 使用带有线程安全的线程。如:继承TimerTask类实现线程,用Timer.schedule启动线程 。

    转载于:https://www.cnblogs.com/DreamRecorder/p/9257603.html

    展开全文
  • 早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是...
  • 有网友提出线程操作个变量不能明确显示是否做到异步操作,现在我们来看下线程操作同一变量,是不是有序的进行呢? #include #include #include int CVICALLBACK ThreadFunction (void *functionData);...
  • 文章目录示例CPU的内存模型Java...那么问题来了:多线程调用access()方法时,接口访问次数统计的结果是否能保证准确呢? 显而易见:不能。 CPU的内存模型 接下来分析一下为什么上面统计的结果会有问题. 我们先来简单理
  • java多线程共享变量

    2018-12-25 15:35:01
    目的:简述java多线程共享变量 共享变量:多个线程都会使用到的同一变量。 Q : 为什么共享变量会造成数据的错误呢??? A : 多个线程在操作共享变量的时候,不是直接在主内存中去操作的。而是都取一个...
  • 只要在静态函数中没有处理多线程共享数据,就不存在着多线程访问同一个静态方法会出现资源冲突的问题,静态方法是否引起线程安全问题主要看该静态方法是否对全局变量(静态变量static member)进行修改操作
  • python,多线程共享全局变量的问题

    千次阅读 2018-07-15 10:12:37
    python,任务中,线程共享全局变量会造成资源抢夺,数据出现异常,要通过jion()、互斥锁等方法解决
  • 多线程执行同一方法问题

    千次阅读 2018-04-09 12:36:36
    前言当线程启动,访问一个实例中的一个方法时,执行情况如何,是等待一个线程执行完成还是同时处理?数据又该如何处理?Java虚拟机运行时数据区的组成由五个部分组成,分别是:方法区,堆,栈,本地方法栈,程序...
  • Java是线程安全的,即对任何方法(包括静态方法)都可以不考虑线程冲突,但有一个前提,就是不能对全局变量共享资源)进行写操作。 如果有,则需要使用同步机制:synchronized关键字。
  • 1. 线程之间是共享全局变量的  验证代码: import threading import time # 定义一个全局变量 g_num = 100 def test1(): # 修改g_num的值 global g_num g_num += 1 print("-----in test1 g_num=%d-...
  • 多线程 3】多线程间的变量共享方式

    千次阅读 热门讨论 2016-10-15 20:29:12
    上篇博客说到了多线程的创建方式,本篇博客说说自己对于多个线程间的共享变量的理解。 一、概述 首先,分析集中不同的变量共享场景: 1,多个线程执行同样的代码 在这种情况下,可以使用同一个Runnable对象(看上一...
  • 当一个进程的线程需要访问同一个变量的时候,就产生了共享变量的问题。可以通过加锁或者信号灯的方式,解决此问题。 解决互斥 - 方法1:加锁 锁LOCK: 通常对互斥资源进行加锁,在加锁之后,可以对互斥资源...
  • 关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块。 同步并不是单单指线程之间的互斥。如果没有同步,一个线程的变化就不能被其他线程看到。同步不仅可以阻止一个线程看到...
  • 1.详解:...2.question:多线程同一对象引用会共享成员变量吗? answer:代码区和数据区是分开的,每个线程独自拥有自己的局部变量。 创建的对象与方法放在堆中,但每个线程有自己的栈存放变量。 ...
  • VC中,多线程和Mutex几个接口函数介绍,以及多线程对全局变量访问时,加mutex和不加mutex两个示例程序及其运行结果。
  • Java多线程操作局部变量与全局变量

    万次阅读 2013-10-18 10:30:04
    在这篇文章里,我们首先阐述什么是同步,不同步有什么问题,然后讨论可以采取哪些措施控制... 说到线程同步,大部分情况下, 我们是在针对“单对象多线程”的情况进行讨论,一般会将其分成两部分,一部分是关于“共享
  • 多线程共享一块内存区域,但是对这块共享区域加锁访问。对调用static变量的方法使用lock或synchronized 《深入理解java虚拟机》知识点 程序运行的时候,内存主要由以下部分组成: 堆: 所有线程共享一...
  • 一、在说明多线程对全局变量的影响之前,我们需要了解下面的一些基础知识: 1、线程是CPU分配时间片的最小单位,通常一个CPU内核可以处理一个线程,如果是双核双线程,那么同一时刻可以处理两个线程。 2、进程是...
  • 模拟ThreadLocal类实现:线程范围内的共享变量,每个线程只能访问他自己的,不能访问别的线程。 package com.ljq.test.thread; import java.util.HashMap; import java.util.Map; import java.util.Random; /** ...
  • 1,加同步锁采用syschronized 关键字到对应的方法或者方法代码块,线程在同一个时刻其实只有一个线程可以访问到共享资源(类似单线程) 2.引入ThreadLocal , 在每个线程中建立一个对应的变量的副本, ...
  • 线程安全一直是程序猿们关注的焦点,多线程也一直是比较让人头疼的话题,想必大家曾经也遇到过各种各种的问题,我就不再累述了。当然,解决方式也有很多,这篇博文给大家提供一种很好的解决线程安全问题的思路。 。...
  • Java多线程之对象及变量的并发访问

    千次阅读 2018-08-06 19:22:51
    synchronized 同步方法 方法内的变量为线程安全,实例变量非线程安全。调用关键字synchronized声明...对于多线程访问同一对象,哪个对象先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,...
  • 同一进程的线程共享的资源和独有的资源
  • 同一进程下线程共享的数据和独有的数据
  • Java多线程编程---线程内数据共享

    千次阅读 2018-04-08 09:19:07
    线程范围内共享数据 线程范围内共享变量要实现的效果为:个对象间共享同一线程内的变量。未实现线程共享变量的demo:public class ThreadScopeDataShare { private static int data = 0; public static void ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 144,236
精华内容 57,694
热门标签
关键字:

多线程共享同一变量