《多线程编程中的原子操作》读后小结

《多线程编程中的原子操作》出自《程序员》杂志2012年第3期,作者陈冠诚

关于原子操作的运用,各个平台或者语言已经提供了非常完整的库,在平时编码时,知道和能用好这些库所提供的API就可以,如,windows API中的atomic库,linux内核中的atomic.h,Java concurrent库中的atomic Integer,C++ 1x中最新支持的atomic_int等。


但明白和理解系统实现原子操作的机制和考虑会让我们做到不但知其然,而且知其所以然,加深对代码的理解。下面是文章中提到的几个考虑因素。


1)对变量的读写是否属于原子操作?

例如:

x=y;

x++;

++x;

x=1;


2)x86 CPU的相关机制,有:

诸如单字节的内存读写

总线锁定时(的内存操作)

缓存一致性原则保证缓存数据的正确


3)变量的字节对齐原则上影响对变量的读写是否为原子操作,但当前的硬件系统和编译器多对此做了优化。所以,有时候,即便没有字节对齐,系统对变量也可以原子读写,但我们应该明白那是系统默默为我们做了优化,平常编码中还是应该尽量做到字节对齐。


4)多线程对位的读写是否安全?




多线程编程中,**栈内存(stack memory)**被认为是线程安全的,主要是因为每个线程拥有独立的栈空间,彼此之间不会直接共享栈内存。这种设计本质上避免了多个线程对同一栈内存区域的并发访问,从而消除了数据竞争和同步问题。 ### 线程栈的独立性 每个线程在创建时都会分配一个独立的运行时栈(通常由操作系统管理),用于存储函数调用时的局部变量、函数参数、返回地址等信息。由于这些栈内存区域是线程私有的,其他线程无法直接访问或修改它们的内容,因此无需使用互斥锁或原子操作来保护这些数据。 这种独立性意味着即使多个线程同时执行相同的函数,它们各自的栈上数据也不会互相干扰。例如,以下代码中,每个线程调用 `foo()` 时,其局部变量 `x` 都位于各自的栈空间中,因此不会产生竞争条件: ```cpp void foo() { int x = 0; // 每个线程都有自己的 x 副本 x++; std::cout << std::this_thread::get_id() << ": " << x << std::endl; } int main() { std::thread t1(foo); std::thread t2(foo); t1.join(); t2.join(); } ``` ### 栈内存与线程安全的关系 尽管栈内存本身是线程安全的,但如果线程通过指针或引用访问了其他线程栈上的数据,则仍然可能导致未定义行为。例如,一个线程将局部变量的地址传递给另一个线程并让其访问该变量,这可能会在原始线程退出后导致访问无效内存[^1]。 因此,栈内存的线程安全性依赖于程序员正确管理生命周期和访问方式。只要不显式共享栈上的数据,每个线程的栈内存就可以被认为是安全的。 ### 对比堆内存 与栈内存不同,堆内存(heap memory)是全局共享的,多个线程可以同时访问同一块堆内存区域,因此需要使用同步机制(如互斥锁、原子操作等)来确保线程安全。例如,当多个线程同时修改一个共享的 `std::vector` 或动态分配的对象时,必须进行适当的同步以防止数据竞争和损坏。 ### 小结内存之所以被认为是线程安全的,是因为: - 每个线程拥有独立的栈空间,彼此之间不共享。 - 局部变量和函数调用信息仅限于当前线程访问。 - 不需要额外的同步机制来保护栈上的数据。 然而,这一特性仅限于栈内存本身,若通过指针或引用访问其他线程的栈数据,则仍然可能引入线程安全问题。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值