Weibw's World Weibw's World
首页
  • HTML
  • Python

    • Python基础知识
    • Python CookBook第三版
    • Flask
  • MySQL

    • MySQL基础知识
    • MySQL调优
    • MySQL面试题
算法
  • FineReport
  • Kettle
  • Git
  • 微信公众号文章
  • 优秀博客文章
  • 其他
收藏夹
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Weibw

一个没有梦想的咸鱼
首页
  • HTML
  • Python

    • Python基础知识
    • Python CookBook第三版
    • Flask
  • MySQL

    • MySQL基础知识
    • MySQL调优
    • MySQL面试题
算法
  • FineReport
  • Kettle
  • Git
  • 微信公众号文章
  • 优秀博客文章
  • 其他
收藏夹
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 《Flask》

  • 《Python Cookbook》第三版

    • 第一章:数据结构与算法

    • 第二章:字符串和文本

    • 第三章:数字日期和时间

    • 第四章:迭代器与生成器

    • 第五章:文件与IO

    • 第六章:数据编码和处理

    • 第七章:函数

    • 第八章:类与对象

    • 第九章:元编程

    • 第十章:模块与包

    • 第十一章:网络与Web编程

    • 第十二章:并发编程

      • 启动与停止线程
      • 判断线程是否已经启动
      • 线程间通信
      • 给关键部分加锁
      • 防止死锁的加锁机制
      • 保存线程的状态信息
        • 问题
        • 解决方案
        • 讨论
      • 创建一个线程池
      • 简单的并行编程
      • Python的全局锁问题
      • 定义一个Actor任务
      • 实现消息发布-订阅模型
      • 使用生成器代替线程
      • 多个线程队列轮询
      • 在Unix系统上面启动守护进程
    • 第十三章:脚本编程与系统管理

    • 第十四章:测试、调试和异常

    • 第十五章:C语言扩展

  • Python基础

  • Python
  • 《Python Cookbook》第三版
  • 第十二章:并发编程
weibw
2022-01-18

保存线程的状态信息

# 问题

你需要保存正在运行线程的状态,这个状态对于其他的线程是不可见的。

# 解决方案

有时在多线程编程中,你需要只保存当前运行线程的状态。 要这么做,可使用 thread.local() 创建一个本地线程存储对象。 对这个对象的属性的保存和读取操作都只会对执行线程可见,而其他线程并不可见。

作为使用本地存储的一个有趣的实际例子, 考虑在8.3小节定义过的 LazyConnection 上下文管理器类。 下面我们对它进行一些小的修改使得它可以适用于多线程:

from socket import socket, AF_INET, SOCK_STREAM
import threading

class LazyConnection:
    def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
        self.address = address
        self.family = AF_INET
        self.type = SOCK_STREAM
        self.local = threading.local()

    def __enter__(self):
        if hasattr(self.local, 'sock'):
            raise RuntimeError('Already connected')
        self.local.sock = socket(self.family, self.type)
        self.local.sock.connect(self.address)
        return self.local.sock

    def __exit__(self, exc_ty, exc_val, tb):
        self.local.sock.close()
        del self.local.sock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

代码中,自己观察对于 self.local 属性的使用。 它被初始化为一个 threading.local() 实例。 其他方法操作被存储为 self.local.sock 的套接字对象。 有了这些就可以在多线程中安全的使用 LazyConnection 实例了。例如:

from functools import partial
def test(conn):
    with conn as s:
        s.send(b'GET /index.html HTTP/1.0\r\n')
        s.send(b'Host: www.python.org\r\n')

        s.send(b'\r\n')
        resp = b''.join(iter(partial(s.recv, 8192), b''))

    print('Got {} bytes'.format(len(resp)))

if __name__ == '__main__':
    conn = LazyConnection(('www.python.org', 80))

    t1 = threading.Thread(target=test, args=(conn,))
    t2 = threading.Thread(target=test, args=(conn,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

它之所以行得通的原因是每个线程会创建一个自己专属的套接字连接(存储为self.local.sock)。 因此,当不同的线程执行套接字操作时,由于操作的是不同的套接字,因此它们不会相互影响。

# 讨论

在大部分程序中创建和操作线程特定状态并不会有什么问题。 不过,当出了问题的时候,通常是因为某个对象被多个线程使用到,用来操作一些专用的系统资源, 比如一个套接字或文件。你不能让所有线程共享一个单独对象, 因为多个线程同时读和写的时候会产生混乱。 本地线程存储通过让这些资源只能在被使用的线程中可见来解决这个问题。

本节中,使用 thread.local() 可以让 LazyConnection 类支持一个线程一个连接, 而不是对于所有的进程都只有一个连接。

其原理是,每个 threading.local() 实例为每个线程维护着一个单独的实例字典。 所有普通实例操作比如获取、修改和删除值仅仅操作这个字典。 每个线程使用一个独立的字典就可以保证数据的隔离了。

编辑 (opens new window)
上次更新: 2023/10/13, 17:39:25
防止死锁的加锁机制
创建一个线程池

← 防止死锁的加锁机制 创建一个线程池→

最近更新
01
牛客网非技术快速入门SQL练习题
03-08
02
其他日常SQL题
03-07
03
用户与权限管理
03-05
更多文章>
Theme by Vdoing | Copyright © 2021-2023 | Weibw | 辽ICP备18015889号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式