目录

  • 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. 【C/C++开发】C语言 DLL(动态链接库)中申请动态内存释放的问题

    参考:首先,声明一点,凡是使用malloc之类命令动态申请的内存,必须进行释放操作,否则就会发生内存泄漏问题. DLL中申请的内存释放,如果没有做过,很可能会认为是直接在调用程序中释放就可以了,其实不 ...

  2. 迭代器iterator和traits编程技法

    前言 这段时间研读SGI-STL-v2.91源码,并提炼核心代码自己实现一遍,感觉受益颇深.觉得有必要写一些文章记录下学习过程的思考,行文旨在总结,会大量参考侯捷<STL源码剖析>的内容. ...

  3. EMR日常操作

    1/ 数据root密码修改后emr配置修改: 2/ 添加s3访问 S3:hadoop-aws-2.8.5.jarjar -tfv hadoop-aws-2.8.5.jar |grep S3FileSy ...

  4. beautifulsoap爬虫

    从html文件读 from bs4 import BeautifulSoup html_doc="文件地址" html_file=open(html_doc,"r&quo ...

  5. python列表和if语句的简单结合

    将列表所有元素打印出来 cars = ['toyota', 'honda', 'mazda', 'nissan', 'mitsubishi', 'subaru', 'suzuki', 'isuzu'] ...

  6. 【转帖】知乎管理华为鸿蒙OS的介绍2

    作者:虎游链接:https://www.zhihu.com/question/328382980/answer/784629132来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  7. java分词工具hanlp介绍

    前几天(6月28日),在第23届中国国际软件博览会上,hanlp这款自然语言处理工具荣获了“2019年第二十三届中国国际软件博览会优秀产品”. HanLP是由一系列模型预算法组成的工具包,结合深度神经 ...

  8. 使用foreach的禁忌

    List<String> list = new ArrayList<>(); Iterator<String> iterator = list.iterator() ...

  9. Linux系列:进阶之jdk、X window安装与使用

    1.安装x window 分为两步: 1.安装 x window,执行指令yum groupinstall “X Window” 2.安装GNOME Desktop,执行指令yum groupinst ...

  10. 初遇PHP(一)

    因为想给自己弄一个微信公众号,顺便提升一下自己,所以有了以下内容,本次学习的最终目标是能用php制作套微信公众号,然后转成Java.为什么要这么麻烦呢,其一是买的资料书是php的,其二是顺水推舟刚好可 ...