多线程 栈空间变量 可见性

本文详细解释了在使用Couchbase回调函数时,为何能够直接操作由不同线程栈上的变量。通过提供一个实际的测试代码,展示了即使在多线程环境下,变量也能被不同线程访问和修改的现象。

今天看couchbase 回调代码的时候发现 callback 函数有一个参数是 cookie,相当于一个调用异步函数时的上下文。

http://docs.couchbase.com/developer/c-2.4/c-intro.html

我们代码里面是类似这样用的。


// 回调函数

static void get_callback(lcb_t instance,
        const void* cookie,
        lcb_error_t code,
        const lcb_get_resp_t* resp) {

//....修改 cookie

}


// instance, install 回调函数

lcb_t cb;

lcb_set_get_callback(cb, get_callback);


// 从couchbase取数据

std::deque<int64_t> uids;

lcb_get(cb, &uids, MAX_ITEMS, pcmds);


因为取数据的线程和调用回调函数的线程是两个不同的线程,

而uids是取数据的线程所在的栈上的,回调函数怎么可以直接操作这个变量呢?

不是一直说同一个进程的多个线程共享堆,但是都有自己的栈么?


恩,是的,这个说法是没错,线程有自己的栈,但是不代表它不能访问其他的线程的栈。


深入理解计算机系统 P663 是这样说的:

“各自独立的线程栈的存储器模型不是那么整齐清楚的。这些栈被保存在虚拟地址空间的栈区域中,并且通常是被相应的线程独立访问的。

我们说通常而不是总是,是因为不同的线程栈是不对其他线程设防的。所以,如果一个线程以某种方式得到一个指向其他线程栈的指针,那么他就可以读写这个栈的任何部分”


下面是测试代码。


#include <thread>
#include <iostream>

using namespace std;

void func(int* pa) {
  cout << pa << endl;
  cout << *pa << endl;
  *pa = 15;
}

int main() {
  int a = 5;
  cout << "main, &a: " << &a << endl;
  thread thrd(func, &a);

  thrd.join();
  cout << "main, &a: " << &a << endl;
  cout << "main, a: " << a << endl;
}

[root@licchen-test-dev001-bjdxt tests]# g++ -std=c++11 -lpthread thread_2.cpp 
[root@licchen-test-dev001-bjdxt tests]# ./a.out 
main, &a: 0x7ffff5ae9564
0x7ffff5ae9564
5
main, &a: 0x7ffff5ae9564
main, a: 15


a 的值被另外的线程修改。



volatile 能够保证多线程变量可见性。底层在给 volatile 变量赋值时,会发送一个 lock 指令,这个指令会锁住特定的内存地址,并将线程中该变量缓存行的数据立即写回主存,并触发总线嗅探机制。读取和修改都会经过总线,总线嗅探机制会嗅探总线上共享变量的改变,如果 CPU 发现自己线程中缓存的该共享变量被修改了,会将该缓存行置为无效状态,然后从主存中再次加载该共享变量的值[^2]。 根据 JMM 规定,所有的共享变量都存储于主内存,每一个线程还存在自己的工作内存,线程对变量的所有操作(读、取)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接访问对方工作内存中的变量,线程之间变量的值的传递需要通过主内存中转来完成。而 volatile 关键字可以保证当一个线程修改了该变量的值时,其他线程能够立即看到修改后的值,从而保证了可见性[^1][^4]。 以下是一个使用 volatile 确保变量值实时更新的代码示例: ```java package com.hmblogs.backend.study.thread; public class VolatileExample { // 假设threadA和threadB同时访问isFlag变量,使用volatile关键字保证可见性 volatile static boolean isFlag = false; public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { System.out.println("Thread 1 is running"); // thread1中执行 isFlag = true; }); Thread thread2 = new Thread(() -> { System.out.println("Thread 2 is running"); // thread2中执行 if (!isFlag) { // 这里的代码可能会执行,即使isFlag已经被thread1设置为true System.out.println("isFlag 是 false."); } }); thread1.start(); thread2.start(); System.out.println("Both threads have finished their execution"); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值