目录

  • Thread对象
  • Lock对象
  • local对象

Thread对象:

多任务可以由多进程完成,也可以由一个进程内的多线程完成。进程是由至少1个线程组成的。

threading模块在较低级的模块 _thread 基础上建立较高级的线程接口。参见: queue 模块。

例子:

import time, threading

def loop():
print('thread %s is running...' % threading.current_thread().name)
time.sleep(2)
print('Main thread is %s' % threading.main_thread().name)
print('thread %s ended.' % threading.current_thread().name) print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended' % threading.current_thread().name)

解释:

threading.current_thread()函数

返回当前线程的实例Thread对象。

threading.main_thread()函数

返回主Thread对象。一般是Python解释器开始时创建的线程。

class threading.Thread(group=Nonetarget=Nonename=Noneargs=()kwargs={}*daemon=None)

这是一个构造器,创造一个thread对象。

  • group用于扩展
  • target指向一个可调用对象,这里是一个函数对象。target会被底层的run()方法引用。
  • args是一个tuple参数,给target使用。
  • kwargs是一个dict, 给target使用。

Thread().is_alive()

返回bol值,判断当前线程是否是存活的。

Thread().name

返回线程对象的名字。

Lock锁对象

多线程和多进程最大的不同:

多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响。

多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

例子:

import time, threading

balance = 0
# lock = threading.Lock() def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n def run_thread(n):
for i in range(1000000):
# lock.acquire()
change_it(n)
# lock.release() t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

结果应当是0,但是未必是0。

原因:

多线程是并行的,即线程是交替运行的。

balance = balance + n这条语句在程序中是分两部分执行:

x = balance + n  #先计算,存入一个临时变量
balance = x #然后将临时变量赋值给balance

t1, t2是交替运行的,不是下面的运行方式:

初始值 balance = 0

t1: x1 = balance + 5 # x1 = 0 + 5 = 5
t1: balance = x1 # balance = 5
t1: x1 = balance - 5 # x1 = 5 - 5 = 0
t1: balance = x1 # balance = 0 t2: x2 = balance + 8 # x2 = 0 + 8 = 8
t2: balance = x2 # balance = 8
t2: x2 = balance - 8 # x2 = 8 - 8 = 0
t2: balance = x2 # balance = 0 结果 balance = 0

而是,交替运行:

初始值 balance = 0

t1: x1 = balance + 5  # x1 = 0 + 5 = 5

t2: x2 = balance + 8  # x2 = 0 + 8 = 8
t2: balance = x2 # balance = 8 t1: balance = x1 # balance = 5
t1: x1 = balance - 5 # x1 = 5 - 5 = 0
t1: balance = x1 # balance = 0 t2: x2 = balance - 8 # x2 = 0 - 8 = -8
t2: balance = x2 # balance = -8 结果 balance = -8

所以需要一个锁定机制,保证当某个线程操作进程内的一个共享变量,其他线程就不能操作。这就是Lock对象的作用。

锁对象

threading.Lock()方法

返回一个锁对象。即创建一把锁。

Lock().acquire() 上锁,Lock().release()  开锁。

就好像在家里(一个进程),多个家人(代表多个线程),你上卫生间,反锁门。其他人(其他线程)就用不了卫生间,只能等你出来再用。

先执行acquire()的线程会获得使用权,使用完成后用release()结束。其他线程在此期间会等待。

缺点:

1 。使用了锁,就相当于多线程变成单线程,效率下降。

2 。由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。

多核cup

如果你不幸拥有一个多核CPU,你肯定在想,多核应该可以同时执行多个线程。

如果写一个死循环的话,会出现什么情况呢?

打开Mac OS X的Activity Monitor,或者Windows的Task Manager,都可以监控某个进程的CPU使用率。

写一个死循环:

import threading, multiprocessing, os

print("%s" % os.getpid())

def loop():
x = 0
while True:
x = x ^ 1 for i in range(multiprocessing.cpu_count()):
t = threading.Thread(target=loop)
t.start()

但实际上,只占用了一个核。如果全占应该是500%。

这是因为Python在解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

小结

多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离,同时,又要小心死锁的发生。

Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。


ThreadLocal

线程本地数据是特定线程的数据。管理线程本地数据,只需要创建一个 local (或者一个子类型)的实例并在实例中储存属性。

class threading.local

思路:1问题->2解决办法->3优化解决办法

1.问题

在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。

但是局部变量也有问题,就是在函数调用的时候,必须要层层传递,很麻烦的!

def process_student(name):
std = Student(name)
# std是局部变量,但是每个函数都要用它,因此必须传进去:
do_task_1(std)
do_task_2(std) def do_task_1(std):
do_subtask_1(std)
do_subtask_2(std) def do_task_2(std):
do_subtask_2(std)
do_subtask_2(std)

如果std作为全局变量?

std = Student(name)

def process_student():
my_std = std
do_task_1()
do_task_2() def do_task_1():
my_std = std def do_task_2():
my_std = std

也不行。因为要多线程工作,每个线程要处理不同的Student对象,std不能被所有的线程共享。

2.解决办法

使用全局dict来存放所有的Student对象,然后以thread自身作为key获得线程对应的Student对象。

这样可以!

import threading

class Student(object):
def __init__(self, name):
self.name = name # 创建全局dict:
g_dict = {} def process_student():
# 获取当前线程关联的student:
std = g_dict[threading.current_thread()]
print('Hello, %s (in %s)' % (std.name, threading.current_thread().name)) def process_thread(name):
# 通过线程对象作为key,来绑定student到g_dict内:
g_dict[threading.current_thread()] = Student(name)
process_student() t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()

问题解决,但这样代码显得不优雅。

3.优化解决办法

使用模块threading自带的class threading.local

它代表线程本地数据的类。

import threading

class Student(object):
def __init__(self, name):
self.name = name # 创建全局ThreadLocal对象:
local_school = threading.local() def process_student():
# 获取当前线程关联的student:
std = local_school.student
print('Hello, %s (in %s)' % (std.name, threading.current_thread().name)) def process_thread(name):
# 绑定ThreadLocal的student:
local_school.student = Student(name)
process_student() t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()

其实就可以把local_school看成是全局变量。但每个属性都是线程的局部变量,线程之间不会干扰,不用管理Lock的问题,ThreadLocal内部会处理。

ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。

具体可以访问底层模块__threading__local

https://github.com/python/cpython/blob/master/Lib/_threading_local.py

问题解决了:

通过ThreadLocal,在一个线程内,参数可以自由的在函数之间调用了。

⚠️本文学习为目的 。主要参考https://www.liaoxuefeng.com/wiki/1016959663602400/1017630786314240

Python:多线程threading模块的更多相关文章

  1. 再看python多线程------threading模块

    现在把关于多线程的能想到的需要注意的点记录一下: 关于threading模块: 1.关于 传参问题 如果调用的子线程函数需要传参,要在参数后面加一个“,”否则会抛参数异常的错误. 如下: for i ...

  2. Python(多线程threading模块)

    day27 参考:http://www.cnblogs.com/yuanchenqi/articles/5733873.html CPU像一本书,你不阅读的时候,你室友马上阅读,你准备阅读的时候,你室 ...

  3. python中threading模块详解(一)

    python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...

  4. python多线程threading.Lock锁用法实例

    本文实例讲述了python多线程threading.Lock锁的用法实例,分享给大家供大家参考.具体分析如下: python的锁可以独立提取出来 mutex = threading.Lock() #锁 ...

  5. python编程中的并发------多线程threading模块

    任务例子:喝水.吃饭动作需要耗时1S 单任务:(耗时20s) for i in range(10): print('a正在喝水') time.sleep(1) print('a正在吃饭') time. ...

  6. [转]python 多线程threading简单分析

    多线程和多进程是什么自行google补脑 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂.所以,这里力图用简单的例子,让你对多线程有个初步的认识. 单线程 在好些年前的 ...

  7. 多线程threading模块

    python的多线程编程 简介 多线程编程技术可以实现代码并行性,优化处理能力,同时功能的更小划分可以使代码的可重用性更好.Python中threading和Queue模块可以用来实现多线程编程. 详 ...

  8. Python多线程 - threading

    目录 1. GIL 2. API 3. 创建子线程 4. 线程同步 4.1. 有了GIL,是否还需要同步? 4.1.1. 死锁 4.1.2. 竞争条件 4.1.3. GIL去哪儿了 4.2. Lock ...

  9. 学会使用Python的threading模块、掌握并发编程基础

    threading模块 Python中提供了threading模块来实现线程并发编程,官方文档如下: 官方文档 添加子线程 实例化Thread类 使用该方式新增子线程任务是比较常见的,也是推荐使用的. ...

随机推荐

  1. Element-UI 框架 el-scrollbar 组件

    Element-UI 框架 el-scrollbar 组件:https://juejin.im/post/5c83d5ac5188257e1c4dc9e7

  2. Python处理字符串和列表元组的小技巧

    变量值互换 a = 1 b = 100 # 变量值互换 a, b = b, a print('a:', a) print('b:', b) 输出结果: a: 100 b: 1 多个变量赋值 a, b, ...

  3. JS触发事件集锦

    事件句柄 HTML 4.0 的新特性之一是有能力使 HTML 事件触发浏览器中的动作(action),比如当用户点击某个 HTML 元素时启动一段 JavaScript.下面是一个属性列表,这些属性可 ...

  4. 18.linux日志收集数据到hdfs上面

    先创建一个目录 在这个job目录下创建upload.sh文件 [hadoop@node1 ~]$ pwd /home/hadoop [hadoop@node1 ~]$ mkdir job [hadoo ...

  5. centos 7 cloudera-manager5.16.2,CDH5.16.2安装升级spark2.4.0

    1.在已经安装好系统的linux服务器上关闭selinux和iptables 2.在所有linux服务器上配置ntp服务并设置ntp时间同步 3.在所有linux服务器上安装好cm和cdh版本所需要对 ...

  6. redis 事务 事务机制详解 MULTI、EXEC、DISCARD、WATCH

    1. Redis服务端是个单线程的架构,不同的Client虽然看似可以同时保持连接,但发出去的命令是序列化执行的,这在通常的数据库理论下是最高级别的隔离2. 用MULTI/EXEC 来把多个命令组装成 ...

  7. 向量运算(lua,三维) 点乘、叉乘、模、夹角

    向量运算在游戏制作中经常用到,稍微总结一下. 一.点乘 如图,假设   向量a与b的点乘表示a在b上的投影与b的模的乘积 公式: 代码: function MathHelper.GetVector3D ...

  8. 【转】js小数转百分比

    转自:js小数和百分数的转换 function toPercent(point){ var str=Number(point*100).toFixed(1); str+="%"; ...

  9. CentOS/RHEL 安装EPEL第三方软件源

    EPEL源简介 EPEL(Extra Packages for Enterprise Linux) 是由 FedORA 社区打造,为 RHEL 及衍生发行版如 CentOS等提供高质量软件包的项目.装 ...

  10. luogu题解P2502[HAOI2006]旅行--最小生成树变式

    题目链接 https://www.luogu.org/problemnew/show/P2502 分析 一个很\(naive\)的做法是从\(s\)到\(t\)双向BFS这当然会TLE 这时我就有个想 ...