Python 多线程之threading介绍

简介: Python 多线程之threading介绍

一、为什么要有多任务?

平时唱歌跳舞都是一起进行的,如果先唱歌后跳舞,或者先跳舞后唱歌,感觉很不协调,别扭—所以需要一个多任务

案例:

8020d34ed72bd7c9daf284ab4eaf5b8.png

from time import sleep
def sing():
    for i in range(3):
        print("正在唱歌....%d"%i)
        sleep(1)
def dance():
    for i in range(3):
        print("正在跳舞....%d"%i)
        sleep(1)
if __name__ == '__main__':
    sing()
    dance()

二、多任务介绍

概念:多任务就是可以一边听歌,一边上网冲浪,或者在干点其它什么的,可以同时有2个以上的任务在执行

注意:

并发:某个时间段内,多个任务交替执行。当有多个线程在操作时,把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状态(暂停)。

并行:同一时间处理多任务的能力,多有多个线程在操作时,CPU同时处理这些线程请求的能力。


三、threading介绍

Python的thread模块是比较底层的,Python的threading模块是对thread做了包装,使用更加方便。


threading常用方法:


方法名 解释

threading.active_count() 返回当前处于active状态的Thread对象

threading.current_thread() 返回当前Thread对象

threading.get_ident() 返回当前线程的线程标识符,线程标识符是一个非负整数,并无特殊含义,知识用来标识线程,该证书可能会被循环利用。Python3.3版本后支持该方法

threading.enumerate() 返回当前处于active解释器的线程对象。Python3.4版本以后支持该方法

threading.main_thread() 返回主线程对象,即启动Python解释器的线程对象。Python3.4版本以后支持该方法

threading.stack_size() 返回创建线程时使用的栈的大小,如果指定size参数,则用来指定后续创建的线程使用的栈大小,size必须是0(标识使用系统默认值)或大于32K正整数

1、Thread类使用说明

threading模块提供了Thread、Lock、RLock、Conditon、Event、Timer和Semaphore等类来支持多线程,Thread是其中最重要也是最基本的一个类,通过该类创建线程并控制线程的运行。


使用Thread创建线程的方法:


  • 为构造函数传递一个可调用对象
  • 集成Thread类并在子类中重写__init__()和run()方法
  • 语法格式:threading.Thread(group=None,target=None,name=None,args=(),kwargs={},*,daemon=None)
  • 参数说明:
  • group:通常默认即可,作为日后扩展ThreadGroup类实现而保留。
  • target:用于run()方法调用的可调用对象,默认为None
  • name:线程名,默认是Thread-N格式构成唯一名称,N是十进制数(正整数)
  • args:用于调用目标函数的关键字参数字典,默认为{}
  • daemon:设置线程是否为守护模式,默认None

threading.Thread的方法和属性:

|方法名 | 解释 |

|–|–|

| start() | 启动线程 |

|run()|线程代码,用来实现线程的功能与业务逻辑,可以在子类中重写该方法来自定义线程的行为|

|init(self,group=None,target=None,name=None,args=(),kwargs=None,daemon=None)|构造函数|

|is_alive()|判断线程是否存活|

|getName()|返回线程名|

|setName()|设置线程名|

|isDaemon()|判断线程是否为守护线程|

|setDaemon()|设置线程是否为守护线程|

|name|用来读取或设置线程的名字|

|ident|线程标识,用非0数字或None(线程未被启动)|

|daemon|线程是否为守护线程,默认false|

|join(timeout=None)|当timeout=None时,会等待至线程结束;当非None时,会等待timeout时间结束,单位秒|

2、实例化threading.Thread(重点)

1)单线程执行 --时间间隔

1ebb544fa523c706d27fbd9288848cb.png

from time import sleep
import datetime
def sing():
    print("正在唱歌....")
    sleep(1)
if __name__ == '__main__':
    for i in range(3):
        sing()
        print(datetime.datetime.now())

2)使用threading模块



import threading
from time import sleep
import datetime
def sing():
    print("正在唱歌....")
    sleep(1)
if __name__ == '__main__':
    for i in range(3):
        t = threading.Thread(target=sing)
        t.start()
        print(datetime.datetime.now())

看执行速度能对比出来threading多线程速度飞快

调用start()才会创建真正的多线程,开始执行代码

3)主线程等待所有子线程结束后才结束

7933292d04e1369fb9c4006dc989522.png


import threading
from time import sleep, ctime
def sing():
    for i in range(3):
        print("正在唱歌....%d" % i)
        sleep(1)
def dance():
    for i in range(3):
        print("正在跳舞....%d" % i)
        sleep(1)
if __name__ == '__main__':
    print("开始:", ctime())
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    t1.start()
    t2.start()
    print("结束: ", ctime())

4)查看线程数

c468de5ec8170c12e81597176239a73.png


import threading
from time import sleep, ctime
def sing():
    for i in range(3):
        print("正在唱歌....%d" % i)
        sleep(1)
def dance():
    for i in range(3):
        print("正在跳舞....%d" % i)
        sleep(1)
if __name__ == '__main__':
    print("开始:", ctime())
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    t1.start()
    t2.start()
    while True:
        num = len(threading.enumerate())
        print("线程数: %d" % num)
        if (num) < 1:
            break
        sleep(1)
    print("结束: ", ctime())

四、继承threading.Thread

1、线程执行封装代码

使用threading线程模块封装时,定义一个新的子类class,继承threading.Thread,重写run方法

15a64c1c8f3fa628aa469f04eebaa04.png

import threading
from time import sleep
class MyClass(threading.Thread):
    def run(self):
        for i in range(3):
            sleep(1)
            msg = self.name + "--->" + str(i)
            print(msg)
if __name__ == '__main__':
    t = MyClass()
    t.start()

Python的threading.Thread类有一个run方法,定义线程功能函数,可以覆盖该方法,创建自己的线程实例后,调用start启动,交给虚拟机进行调度,有执行机会就会调用run方法执行线程。

2、线程执行顺序


52ed0aa3e5e356e56d46665a65bff03.png

import threading
from time import sleep
class MyClass(threading.Thread):
    def run(self):
        for i in range(3):
            sleep(1)
            msg = self.name + "--->" + str(i)
            print(msg+'\n')
def test():
    for i in range(5):
        t = MyClass()
        t.start()
if __name__ == '__main__':
    test()

多线程执行顺序是不确定的,sleep时,线程将被阻塞(Blocked),到sleep结束后,线程进入就绪(Runnable)状态,等待调度。线程调度选择一个线程执行。以上代码能保证运行每个线程都运行完整的run函数,但是线程启动顺序、run函数每次循环执行的顺序不能确定。


总结:


每个线程默认有一个名字

当线程run方法结束时,该线程完成。

无法控制线程调度顺序,可以通过其它办法影响线程调度方式。

五、多线程共享全局变量(重点)


0a0c2c6b69ec4105b5a84df495f6ff1.png

from threading import Thread
from time import sleep
gl_num = 10
def sing():
    global gl_num
    for i in range(3):
        gl_num += 1
    print("sing------>%d"%gl_num)
def dance():
    global gl_num
    print("sing------>%d" % gl_num)
t1 = Thread(target=sing)
t1.start()
sleep(1)
t2 = Thread(target=dance)
t2.start()

列表当做参数传递:

076edb736ebbca28010e0fef94ad2c5.png

from threading import Thread
from time import sleep
gl_num = [10, 20, 30]
def sing(num):
    num.append(33)
    print("sing------>", num)
def dance(num):
    sleep(1)
    print("sing------>", num)
t1 = Thread(target=sing, args=(gl_num,))
t1.start()
t2 = Thread(target=dance, args=(gl_num,))
t2.start()

同进程内的所有线程共享全局变量,方便多个线程共享数据

缺点,线程一单被修改造成多线程之间全局变量混乱现象(使用时保证全局变量不能被修改,线程非安全的)

六、线程同步和锁(重点)

1、线程同步

概念:协同步调,按预定的先后顺序执行,多线程修改全局变量,可能会出现意外结果,为了保证数据的正确性,需要对多个线程进行同步。

方法:使用Thread对象的Lock和Rlock实现简单线程同步,acquire和release方法对只允许一个线程操作的数据,可以将其放到acquire和release之间。

2、互斥锁

某个线程需要修改共享数据时,先将其锁定,此时资源状态为锁定,其它线程不允许修改;直到该线程释放资源,将资源状态调整非锁定,其它线程才能再次锁定该共享数据。互斥锁保证每次只有一个线程进行数据修改,能够保证多线程数据正确性。


threading模块中定义Lock类,方便的处理锁:

import threading
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 解锁
mutex.release()

调用acquire之前没有锁,不会堵塞;已锁,会堵塞,直到这个锁解锁


案例:互斥锁两个线程对统一变量累加10万次



import threading
from time import sleep
gl = 0
mutex = threading.Lock()
def sing(num):
    global gl
    for i in range(num):
        mutex.acquire()
        gl += 1
        mutex.release()
    print("唱歌-----> %d" % gl)
def dance(num):
    global gl
    for i in range(num):
        mutex.acquire()
        gl += 1
        mutex.release()
    print("跳舞------>%d" % gl)
t1 = threading.Thread(target=sing, args=(100000,))
t1.start()
t2 = threading.Thread(target=dance, args=(100000,))
t2.start()
while len(threading.enumerate()) != 1:
    sleep(1)
print("最终结果: %d" % gl

锁的拆解过程:


当一个线程调用acquire()方法时锁定,状态是locked

-只允许有一个线程获得锁,另一个线程想使用,该线程变成blocked状态,阻塞,直至有锁的线程调用release()方法释放锁,此时变成unlocked状态。

-线程调度从阻塞线程中选择一个来调用锁,并使此线程进入运行running状态。

总结:


锁好处:确保了代码可以由某一线程从开始到结束独立运行

锁坏处:阻止了多线程并发执行,单线程执行效率低下;使用多个锁可能会造成死锁


3、死锁

解释:线程间共享多个资源时,两个线程并各站一部分资源,同时等待对方释放资源,会造成死锁。


如何避免死锁:


程序设计时尽量避免

添加超时时间等


相关文章
|
7天前
|
开发工具 计算机视觉 Python
大恒相机 - Python 多线程拍摄
大恒相机 - Python 多线程拍摄
19 1
|
9天前
|
调度 Python
|
11天前
|
Shell Python
Python多线程怎么做?
Python 3 中利用 `threading` 模块实现多线程。创建与执行线程有两种常见方式:一是直接使用 `Thread` 类实例,指定目标函数;二是通过继承 `Thread` 类并重写 `run` 方法。前者构造 `Thread` 对象时通过 `target` 参数指定函数,后者则在子类中定义线程的行为。两种方式均需调用 `start` 方法启动线程。示例展示了这两种创建线程的方法及输出顺序,体现线程并发执行的特点。
|
27天前
|
存储 Python 容器
Node中的AsyncLocalStorage 使用问题之在Python中,线程内变量的问题如何解决
Node中的AsyncLocalStorage 使用问题之在Python中,线程内变量的问题如何解决
|
7天前
|
SQL 机器学习/深度学习 算法
【python】python指南(一):线程Thread
【python】python指南(一):线程Thread
22 0
|
16天前
|
数据采集 并行计算 程序员
Python中的并发编程:理解多线程与多进程
在Python编程中,理解并发编程是提升程序性能和效率的关键。本文将深入探讨Python中的多线程和多进程编程模型,比较它们的优劣势,并提供实际应用中的最佳实践与案例分析。
|
3月前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
3月前
|
缓存 负载均衡 安全
在Python中,如何使用多线程或多进程来提高程序的性能?
【2月更文挑战第17天】【2月更文挑战第50篇】在Python中,如何使用多线程或多进程来提高程序的性能?
46 4
|
3月前
|
安全 Python
Python中的并发编程:多线程与多进程技术探究
本文将深入探讨Python中的并发编程技术,重点介绍多线程和多进程两种并发处理方式的原理、应用场景及优缺点,并结合实例分析如何在Python中实现并发编程,以提高程序的性能和效率。
|
3月前
|
数据采集 数据库 C++
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
44 0