首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【测试开发】python系列教程:threading模块

【测试开发】python系列教程:threading模块

作者头像
雷子
发布于 2023-08-21 19:56:51
发布于 2023-08-21 19:56:51
5460
举报

上一篇

【测试开发】python系列教程:logging日志模块

这次我们分享python的threading库。


什么是线程(thread)?

线程和进程容易混淆,可以通过下面的几句话来理解:

代码语言:javascript
复制
进程是资源分配的最小单位,一个程序至少有一个进程。如:浏览器

线程是程序执行的最小单位,一个进程至少有一个线程。如:浏览器播放视频

进程都有自己独立的地址空间,内存,数据栈等,所以进程占用资源多。由于进程的资源独立,所以通讯不方便,只能使用进程间通讯(IPC)。

线程共享进程中的数据,他们使用相同的地址空间,使用线程创建快捷,创建开销比进程小。同一进程下的线程共享全局变量、静态变量等数据,所以线程通讯非常方便,但会存在数据同步与互斥的问题,如何处理好同步与互斥是编写多线程程序的难点。

一个进程中可以存在多个线程,在单核CPU中每个进程中同时刻只能运行一个线程,只有在多核CPU中才能存在线程并发的情况。

当线程需要运行但没有运行空间时,会对线程的优先级进行判断,高优先级先运行,低优先级进程让行。

threading 模块的Thread 类是主要的执行对象。使用Thread 类,可以有很多方法来创建线程。最常用的有下面:

代码语言:javascript
复制
创建Thread 的实例,传给它一个可调用对象(函数或者类的实例方法)。
派生Thread 的子类,并创建子类的实例。

那么我门来实现一个简单的线程

代码语言:javascript
复制
from threading import Thread
from time import sleep, ctime

def functhread(name, secd):
    print('---开始---', name, '时间', ctime())
    sleep(secd)
    print('---结束---', name, '时间', ctime())
# 创建 Thread 实例
t1 = Thread(target=functhread, args=('第一个', 1))
t2 = Thread(target=functhread, args=('第二个', 2))
# 启动线程运行
t1.start()
t2.start()

# 等待所有线程执行完毕
t1.join()  # join() 等待线程终止,要不然一直挂起
t2.join()

结果:

Thread 实例化时需要接收 target,args(kwargs)两个参数。

target 用于接收需要使用多线程调用的对象。

args 或 kwargs 用于接收调用对象的需要用到的参数,args接收tuple,kwargs接收dict。

start() 是方法用来启动线程的执行。

join() 方法是一种自旋锁,它用来等待线程终止。也可以提供超时的时间,当线程运行达到超时时间后结束线程,如join(500),500毫秒后结束线程运行。

注意:如果当你的主线程还有其他事情要做,而不是等待这些线程完成,就可以不调用join()。join()方法只有在你需要等待线程完成然后在做其他事情的时候才是有用的。

is_alive () 线程是否存活 isDaemon() 是否为守护线程 setDaemon(daemonic) 设置为守护线程

第二个方式创建:继承Thread,重写run

代码语言:javascript
复制
class NewThread(Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        '''
        重写Thread类里面的run方法
        里面写入你需要执行的代码
        之后使用start()方法就可以调用run
        '''
        print('---开始---', '时间', ctime())
        print('---结束---', '时间', ctime())
my_thread = NewThread()
my_thread.start() # 执行线程

结果:

threding还可以输出

代码语言:javascript
复制
print(threading.current_thread())#当前线程
print(threading.main_thread())#主线程
print(threading.active_count())#活跃线程数
print(threading.enumerate())#返回一个包含正在运行的线程的list。

看下结果

代码语言:javascript
复制
class NewThread(Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        '''
        重写Thread类里面的run方法
        里面写入你需要执行的代码
        之后使用start()方法就可以调用run
        '''
        print('---开始---', '时间', ctime())
        print('---结束---', '时间', ctime())
my_thread = NewThread()
my_thread.start() # 执行线程
print(threading.current_thread())#当前线程
print(threading.main_thread())#主线程
print(threading.active_count())#活跃线程数
print(threading.enumerate())#返回一个包含正在运行的线程的list。
my_thread.join()

打印下结果:

假如我们join放在前面

前面进程执行完,才执行打印,那么久只有一个主线程,同时也可以看到,我们的子线程是启动成功了。

threading还有其他的类对象和方法如下:

1.threading 模块的类对象

代码语言:javascript
复制
Thread 执行线程
Timer 在运行前等待一段时间的执行线程
Lock 原语锁(互斥锁,简单锁)
RLock 重入锁,使单一线程可以(再次)获得已持有的锁
Condition 条件变量,线程需要等待另一个线程满足特定条件
Event 事件变量,N个线程等待某个事件发生后激活所有线程
Semaphore 线程间共享资源的寄存器
BoundedSemaphore 与Semaphore 相似,它不允许超过初始值
Barrie 执行线程达到一定数量后才可以继续

2. threading 模块的函数

代码语言:javascript
复制
activeCount() 获取当前活动中的Thread对象个数
currentThread() 获取当前的Thread对象
enumerate() 获取当前活动的Thread对象列表
settrace(func) 为所有线程设置一个跟踪(trace)函数
setprofile(func) 为所有线程设置配置文件(profile)函数
stack_size(size=None) 获取新创建线程的栈大小,也可设置线程栈的大小为size。

关于锁的使用,我们在前面也分享了,在线程的时候,是共享的,如果我们有一个变量,那么在线程之间的调用,也是共享的,可能会造成了数据错误,比如,我们要判断一个数字在大于0的时候才会减少,如果没有锁的存在

代码语言:javascript
复制
 num=10
def numsll():
    global  num
    if num>0:
        print("num:{}".format(num))
        num-=1

def main():
    threads = []
    for i in range(0,20):
        t = threading.Thread(target=numsll)
        threads.append(t)
    for i in range(0,20):
        threads[i].start()

if __name__=="__main__":
    main()       

可以看到在扣减的时候出现了错误

两个线程同时扣减,就会出现两个9正在扣减,那么我们如何做呢,我们可以用锁来解决

代码语言:javascript
复制

lock=threading.Lock()
num=10
def numsll():
    global  num
    lock.acquire()
    if num>0:

        print("num:{}".format(num))
        num-=1
    lock.release()
def main():
    threads = []
    for i in range(0,20):
        t = threading.Thread(target=numsll)
        threads.append(t)
    for i in range(0,20):
        threads[i].start()

if __name__=="__main__":
    main()
    

这样如论怎么运行

在我们日常中,比如出现超卖的情况,很多时候可以通过考锁来处理。当然线程模块中还有其他的类,这里我们今天就分享了两个,一个是创建线程,一个是在线程中如何避免共享数据之间的错乱的情况,保证正确性。

如果觉得这篇文章还不错,来个【分享、点赞、在看】三连吧,让更多的人也看到~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 什么是线程(thread)?
  • threading还有其他的类对象和方法如下:
  • 2. threading 模块的函数
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档