请问大家有推荐的 CPython 相关教程或书籍吗?

请问大家有推荐的 CPython 相关教程或书籍吗?我希望找一套讲解 Python 虚拟机和 Python 编译器源码的教程学习。

  • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/7426871
  • 你也可以参考下这篇文章:CPython的命令行与python文件的执行
  • 除此之外, 这篇博客: 面向 CPython GIL 的多线程编程要点中的 Python 的线程库锁 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 我们再举个例子看看非原子操作下,怎么保证线程安全。

    >>> n = 0
    >>> def foo():
    ...     global n
    ...     n += 1
    ...
    >>> import dis
    >>> dis.dis(foo)
      3           0 LOAD_GLOBAL              0 (n)
                  3 LOAD_CONST               1 (1)
                  6 INPLACE_ADD
                  7 STORE_GLOBAL             0 (n)
                 10 LOAD_CONST               0 (None)
                 13 RETURN_VALUE
    

    代码编译后的字节码指令:

    1. 将全局变量 n 的值 load 到堆栈
    2. 将常数 1 的值 load 到堆栈
    3. 在堆栈顶部将两个数值相加
    4. 将相加结果存储回全局变量 n 的地址
    5. 将常数 0(None) 的值 load 到堆栈
    6. 从堆栈顶部返回常数 0 给函数调用者

    语句 n += 1 被编译成了前 4 个字节码,后两个字节码是 foo 函数的 return 操作,解释器自动添加。

    我们在上文提到,Python2 的线程每执行 1000 个字节码就会被动的让出 GIL。现在假如字节码指令 INPLACE_ADD 就是那第 1000 条指令,这时本应该继续执行 STORE_GLOBAL 0 (n) 存储到 n 地址的数据就被驻留在了堆栈中。如果同一时刻,变量 n 被别的处理器当前线程中的代码调用了。那么请问现在的 n 还是 +=1 之后的 n 吗?答案是此时的 n 发生了更新丢失,在两个当前线程中的 n 已经不是同一个 “n” 了。这就是上面我们提到过的内存可见性数据安全问题的又一个佐证。

    下面的代码正确输出为 100,但在 Python 多线程多处理器场景中,可能会得到 99 或 98 的结果。

    import threading
    
    n = 0
    threads = []
    
    
    def foo():
        global n
        n += 1
    
    
    for i in range(100):
        t = threading.Thread(target=foo)
        threads.append(t)
    
    
    for t in threads:
        t.start()
    
    
    for t in threads:
        t.join()
    
    
    print(n)
    

    此时,Python 程序员应该要想到使用 Python 线程库的锁来解决为。

    import threading
    
    n = 0
    lock = threading.Lock()
    threads = []
    
    
    def foo():
        global n
        with lock:
            n += 1
    
    
    for i in range(100):
        t = threading.Thread(target=foo)
        threads.append(t)
    
    
    for t in threads:
        t.start()
    
    
    for t in threads:
        t.join()
    
    
    print(n)
    

    显然,即便 Python 已经存在了 GIL,但依旧要求程序员坚持 “始终为共享可变状态的读写上锁”。