对于多线程访问同一变量是否需要加锁的问题

本文通过实验验证了32位CPU与内存的最小交换数据为4字节/次,探讨了不同数据类型在多线程环境下的读写行为及锁的使用策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于多线程访问同一变量是否需要加锁的问题,先前大家都讨论过。今天用代码验证了一下之前的猜想: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;
### Python多线程同时访问共享变量的处理方法 在Python中,当多个线程同时访问同一个共享变量时,可能会引发数据竞争(Race Condition),从而破坏程序的数据一致性。为了确保线程安全并正确管理共享变量,可以采用以下几种常见的策略。 #### 使用锁机制 (Locks) 通过引入`threading.Lock()`对象,可以在特定代码块执行期间阻止其他线程对该资源的操作,从而实现同步控制。这种方式能够有效防止多个线程同时修改共享变量的情况发生[^1]。 ```python import threading shared_variable = 0 lock = threading.Lock() def increment(): global shared_variable with lock: # 加锁操作 temp = shared_variable temp += 1 shared_variable = temp threads = [] for _ in range(10): t = threading.Thread(target=increment) threads.append(t) t.start() for t in threads: t.join() print(f"Final value of shared variable is {shared_variable}") ``` 以上代码展示了如何利用锁来保护对`shared_variable`的更新过程,避免因并发引起的错误行为。 #### 利用信号量(Semaphores) 或条件变量(Condition Variables) 除了基本的锁外,还可以借助更高级别的同步原语如信号量或者条件变量来进行复杂的协调工作。这些工具允许开发者定义更加精细的行为模式,在某些场景下可能更为适用[^1]。 #### ThreadLocal存储方式 另一种解决办法是使用`threading.local()`创建独立于各线程的空间存放各自版本的数据副本。这样即使不同线程间存在同名变量也不会相互影响,因为它们实际上指向不同的内存位置[^3]。 ```python import threading local_data = threading.local() local_data.value = None def thread_task(value): local_data.value = value print(f"{threading.current_thread().name} has its own copy:", local_data.value) t1 = threading.Thread(target=thread_task, args=(1,), name="Thread-1") t2 = threading.Thread(target=thread_task, args=(2,), name="Thread-2") t1.start(); t2.start(); t1.join(); t2.join(); # Output will show each thread having separate values. ``` 此方案特别适合那些不需要跨线程通信却希望简化开发流程的应用场合。 #### 注意事项 尽管上述技术可以帮助构建健壮可靠的多线程应用程序,但在实际应用过程中仍需注意以下几点: - **性能开销**:频繁加解锁可能导致显著增加运行时间成本; - **死锁风险**:如果设计不当容易造成两个及以上进程互相等待对方释放资源的现象; - **调试困难度提升**:相较于单一线程逻辑而言,追踪定位潜在问题往往更具挑战性; 综上所述,合理选用合适的手段对于保障Python多线程环境下共享变量的安全至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值