在日常的开发中经常会用到多线程和多进程编程,使用多线程编程可降低程序的复杂度,使程序更简洁高效。
线程是程序执行流的最小单元,是进程的一个实体,一个进程可以拥有多个线程,多个线程可以共享进程所拥有的资源。
线程可以提升程序的整体性能,一般分为内核线程和用户线程,内核线程由操作系统内核创建和撤销,用户线程不需要内核支持,是在用户程序中实现的线程。
需要注意的是:线程不能独立运行,他必须依附于进程;线程可以被抢占(中断)和暂时搁置(休眠)。
在 python3 中有两种实现多线程编程的模块。
其实python并不适合做多线程的开发,因为 python 解释器使用了内部的全局解释器锁(GIL锁),使得在无论何时计算机只会允许在处理器上运行单个线程,即便多核GPU也是如此(即GIL锁同一时刻只允许一个进程只有一个线程被CPU调度),所以它的效率较其他语言低。
threading 模块创建线程的方式与 Java 类似,都是采用的类来创建线程。
threading模块的函数:
函数 | 说明 |
---|---|
threading.active_count() | 返回正运行的线程数量(包括主线程),与len(threading.enumerate())结果相同 |
threading.current_thread() | 返回当前线程的变量 |
threading.main_thread() | 返回主线程 |
threading.enumerate() | 返回一个正在运行的线程的列表 |
方法一:
在创建线程时必须先定义一个继承threading.Thread的类,然后重写子类中的run()方法,使用
start()方法启动线程。
import threading
class threadTest(threading.Thread): #类必须继承threading.Thread
def __init__(self,args) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(threadTest,self).__init__() #初始化super()内的必须与类名一样
self.args = args
def run(self) -> None: #定义run()方法,主要写线程的执行内容
print('test thread running......')
print('args:',self.args)
return super().run()
if __name__ == '__main__':
test = threadTest('just test') #实例化
test.start() #启动线程,即运行run()方法
方法二:
使用函数的方式创建线程。
import threading
def threadTest(arg):
print('test thread running......')
print('args:',arg)
if __name__ == '__main__':
# target 传入函数名,注意不要写参数;args target传入的函数需要传入的参数,注意传入参数以元组的形式
thread = threading.Thread(target=threadTest,args=('just test',))
thread.start() #启动线程
两种方法的结果都一样:
test thread running......
args: just test
需要注意的是:创建的线程为子线程,是主线程创建的子线程。
除 start() 方法外,threading.Thread类还提供了以下方法:
方法 | 说明 |
---|---|
join(timeout) | 表示主线程等待子线程timeout时长(s)后子线程若还未结束,就强制结束子线程,不设置则主线程会一直等待子线程结束后再结束 |
getName() | 获取线程名 |
setName() | 设置线程名 |
isAlive() | 返回线程是否正在运行 |
ident() | 获取线程标识符 |
setDaemon(bool) | 设置主,子线程运行时的关系。bool为True时主线程结束,子线程立即结束;为false主,子线程运行毫不相关,独立运行 |
jion()常用于控制线程的运行方法,只能在线程启动后使用,设置主线程是否等待子线程的结束。
未使用join()方法:
import threading
import time
class threadTest(threading.Thread): #类必须继承threading.Thread
def __init__(self) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(threadTest,self).__init__() #初始化super()内的必须与类名一样
def run(self) -> None: #定义run()方法,主要写线程的执行内容
print('子线程' + self.getName() + '开始:' + str(time.time()))
time.sleep(1) #休眠1s
print('子线程' + self.getName() + '结束:' + str(time.time()))
return super().run()
if __name__ == '__main__':
threads = [] #创建线程列表
for i in range(1,5):
test = threadTest()
test.setName('Thread-%s'%(str(i))) #设置线程名称
threads.append(test)
for thread in threads:
thread.start() #启动线程
print('主线程已结束:%s'%(str(time.time())))
运行结果:
子线程Thread-1开始:1652286540.3881922
子线程Thread-2开始:1652286540.3883848
子线程Thread-3开始:1652286540.388582
子线程Thread-4开始:1652286540.3887677
主线程已结束:1652286540.3887904
子线程Thread-1结束:1652286541.3895423
子线程Thread-2结束:1652286541.3897188
子线程Thread-3结束:1652286541.3898888
子线程Thread-4结束:1652286541.3902392
可以看到未使用join()方法时,多个子线程几乎是同时创建,同时结束的,并且主线程并没有等子线程结束就已经结束了。
下面是使用了join()方法。
import threading
import time
class threadTest(threading.Thread): #类必须继承threading.Thread
def __init__(self) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(threadTest,self).__init__() #初始化super()内的必须与类名一样
def run(self) -> None: #定义run()方法,主要写线程的执行内容
print('子线程' + self.getName() + '开始:' + str(time.time()))
time.sleep(1) #休眠1s
print('子线程' + self.getName() + '结束:' + str(time.time()))
return super().run()
if __name__ == '__main__':
threads = [] #创建线程列表
for i in range(1,5):
test = threadTest()
test.setName('Thread-%s'%(str(i))) #设置线程名称
threads.append(test)
for thread in threads:
thread.start() #启动线程
thread.join()
print('主线程已结束:%s'%(str(time.time())))
运行结果:
子线程Thread-1开始:1652286834.7727253
子线程Thread-1结束:1652286835.7734482
子线程Thread-2开始:1652286835.7740881
子线程Thread-2结束:1652286836.7755718
子线程Thread-3开始:1652286836.7760375
子线程Thread-3结束:1652286837.7775867
子线程Thread-4开始:1652286837.778077
子线程Thread-4结束:1652286838.779412
主线程已结束:1652286838.7796373
可以看到使用了join()后线程是依次运行的,且主线程是等待子线程结束后再结束的。
setDaemon(bool)方法用于设置主线程的守护线程,在启动线程启动之前使用。
当bool为True时,该线程为守护线程,主线程结束,子线程立即结束;为false主,子线程运行毫不相关,独立运行,子线程继续运行到结束。示例如下:
import threading
import time
class threadTest(threading.Thread): #类必须继承threading.Thread
def __init__(self) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(threadTest,self).__init__() #初始化super()内的必须与类名一样
def run(self) -> None: #定义run()方法,主要写线程的执行内容
print('子线程' + self.getName() + '开始:' + str(time.time()))
time.sleep(3) #休眠2s
print('子线程' + self.getName() + '结束:' + str(time.time()))
return super().run()
if __name__ == '__main__':
print('主线程开始:%s'%(str(time.time())))
thread = threadTest()
thread.setName('Thread-1') #设置线程名称
thread.setDaemon(False) #参数设置为False
thread.start() #启动线程
time.sleep(1)
print('主线程已结束:%s'%(str(time.time())))
运行结果:
主线程开始:1652287657.6037383
子线程Thread-1开始:1652287657.6039832
主线程已结束:1652287658.6050904
子线程Thread-1结束:1652287660.6057055
可以看到bool为false主线程结束,子线程继续运行到结束。
import threading
import time
class threadTest(threading.Thread): #类必须继承threading.Thread
def __init__(self) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(threadTest,self).__init__() #初始化super()内的必须与类名一样
def run(self) -> None: #定义run()方法,主要写线程的执行内容
print('子线程' + self.getName() + '开始:' + str(time.time()))
time.sleep(3) #休眠2s
print('子线程' + self.getName() + '结束:' + str(time.time()))
return super().run()
if __name__ == '__main__':
print('主线程开始:%s'%(str(time.time())))
thread = threadTest()
thread.setName('Thread-1') #设置线程名称
thread.setDaemon(True) #参数设置为True
thread.start() #启动线程
time.sleep(1)
print('主线程已结束:%s'%(str(time.time())))
运行结果:
在这里插主线程开始:1652287817.3947828
子线程Thread-1开始:1652287817.3950415
主线程已结束:1652287818.396119入代码片
可以看到bool为True时,主线程一结束,子线程就立即结束。
多线程下一篇传送门:
https://blog.csdn.net/youngwyj/article/details/124833126