多线程
一、多线程理论
[1]什么线程
- 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程
- 线程顾名思义,就是一条流水线工作的过程
- 一条流水线必须属于一个车间,一个车间的工作过程是一个进程
- 车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线
- 流水线的工作需要电源,电源就相当于CPU
- 所以进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是CPU上的执行单位。
- 多线程(即多个控制线程)的概念是在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
- 例如
- 北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。
[小结]
- 每一个进程必定自带一个线程
- 进程:资源单位
- 起一个进程仅仅只是 在内存空间中开辟出一块独立的空间
- 线程:执行单位
- 真正被CPU执行的其实是进程里面的线程
- 线程指的就是代码的执行过程,执行代码中所需要使用到的资源都找所在的进程索要
- 进程和线程都是虚拟单位,只是为了我们更加方便的描述问题
[2]线程的创建开销
(1)创建进程的开销要远大于创建线程
- 如果我们的软件是一个工厂
- 该工厂有多条流水线
- 流水线工作需要电源
- 电源只有一个即CPU(单核CPU)
- 一个车间就是一个进程
- 一个车间至少一条流水线(一个进程至少一个线程)
- 创建一个进程
- 就是创建一个车间(申请空间,在该空间内建至少一条流水线)
- 而建线程
- 就只是在一个车间内造一条流水线
- 无需申请空间,所以创建开销小
- 一个车间就是一个进程
(2)进程之间是竞争关系,线程之间是协作关系
- 车间直接是竞争/抢电源的关系,竞争
- 不同的进程直接是竞争关系
- 不同的程序员写的程序运行的迅雷抢占其他进程的网速
- 把其他进程当做病毒干死
- 一个车间的不同流水线式协同工作的关系
- 同一个进程的线程之间是合作关系,是同一个程序写的程序内开启动
- 迅雷内的线程是合作关系,不会自己干自己
二、多线程操作
[1]threading
模块介绍
multiprocess
模块的完全模仿了threading模块的接口- 二者在使用层面,有很大的相似性,因而不再详细介绍
[2]开启线程的两种方式
- 开启线程不需要在main下面执行代码,直接书写即可
- 但是我们还是习惯性的将启动命令写在main下面
(1)方式一:直接调用 Thread 方法
from threading import Thread
import time
def run(i):
print(f'这里是子线程{i}开始!')
time.sleep(1)
print(f'这里是子线程{i}结束!')
if __name__ == '__main__':
print('主程序开始运行!')
for i in range(1, 3):
t = Thread(target=run, args=(i,))
t.start()
t.join()
print('主程序结束运行!')
"""
主程序开始运行!
这里是子线程1开始!
这里是子线程1结束!
这里是子线程2开始!
这里是子线程2结束!
主程序结束运行!
"""
(2)方式二:继承 Thread 父类
from threading import Thread
import time
class MyThread(Thread):
def __init__(self, i):
super().__init__()
self.i = i
def run(self):
print(f'这里是子线程{self.i}开始!')
time.sleep(1)
print(f'这里是子线程{self.i}结束!')
def main():
print('主程序开始运行!')
for i in range(1, 3):
t = MyThread(i)
t.start()
t.join()
print('主程序结束运行!')
if __name__ == '__main__':
main()
"""
主程序开始运行!
这里是子线程1开始!
这里是子线程1结束!
这里是子线程2开始!
这里是子线程2结束!
主程序结束运行
"""
[3]一个进程下开启多个线程和多个子进程的区别
(1)开启速度的比较
- 线程的开启速度较快
from multiprocessing import Process
from threading import Thread
import time
def run():
print('开启成功!')
def timer(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, *kwargs)
print(f'所用时间{time.time() - start_time}s')
return res
return inner
@timer
def the_process():
p = Process(target=run)
p.start()
p.join()
@timer
def the_thread():
p = Thread(target=run)
p.start()
p.join()
if __name__ == '__main__':
the_process()
# 开启成功!
# 所用时间0.19514107704162598s
the_thread()
# 开启成功!
# 所用时间0.001990795135498047s
(2)多线程与有进程的PID
import os
from multiprocessing import Process
from threading import Thread
def run():
print('开启成功!', os.getpid())
def the_process():
p1 = Process(target=run)
p2 = Process(target=run)
p1.start()
p2.start()
p1.join()
p2.join()
def the_thread():
p1 = Thread(target=run)
p2 = Thread(target=run)
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
the_process()
# 开启成功! 21372
# 开启成功! 11148
# 多个进程就会有多个不同的pid
the_thread()
# 开启成功! 14972
# 开启成功! 14972
# 多个线程就使用通一个pid
[4]线程对象的join方法
- 线程的join方法和进程的join方法是一样的
from threading import Thread
import time
def run():
print('子线程开始!')
time.sleep(2)
print('子线程结束!')
def the_thread():
p = Thread(target=run)
p.start()
p.join()
if __name__ == '__main__':
print('主程序开始运行!')
the_thread()
print('主程序结束运行!')
"""
主程序开始运行!
子线程开始!
子线程结束!
主程序结束运行!
"""
[5]线程对象属性及其他方法
(1)获取当前进程的名字current_thread
from threading import Thread, current_thread
def run():
print('子线程开始!')
print(f'当前运行的程序名:{current_thread().name}')
print('子线程结束!')
def main():
t = Thread(target=run)
t.start()
t.join()
print(f'当前运行的程序名:{current_thread().name}')
if __name__ == '__main__':
main()
"""
子线程开始!
当前运行的程序名:Thread-1 (run)
子线程结束!
当前运行的程序名:MainThread
"""
(2)统计当前活跃的线程数active_count
from threading import Thread, current_thread, active_count
def run():
print('子线程开始!')
print(f'当前运行的程序名:{current_thread().name}')
print('子线程结束!')
def main():
t = Thread(target=run)
t.start()
t.join()
print(f'当前活跃的线程数: {active_count()}')
print(f'当前运行的程序名:{current_thread().name}')
if __name__ == '__main__':
main()
"""
子线程开始!
当前运行的程序名:Thread-1 (run)
子线程结束!
当前活跃的线程数: 1
当前运行的程序名:MainThread
"""
三、守护线程
[1]主线程死亡,子线程未死亡
- 主线程结束运行后不会马上结束,而是等待其他非守护子线程结束之后才会结束
- 如果主线程死亡就代表者主进程也死亡,随之而来的是所有子线程的死亡
from threading import Thread
import time
def run():
print('子进程开始!')
time.sleep(1)
print('子进程结束!')
if __name__ == '__main__':
print('主程序开始运行!')
t = Thread(target=run)
t.start()
print('主进程结束运行!')
"""
主程序开始运行!
子进程开始!
主进程结束运行!
子进程结束!
"""
[2]主线程死亡,子线程也死亡
from threading import Thread
import time
def run():
print('子进程开始!')
time.sleep(1)
print('子进程结束!')
if __name__ == '__main__':
print('主程序开始运行!')
t = Thread(target=run)
# 开启守护线程
t.daemon = True
t.start()
print('主进程结束运行!')
# 当主线程死亡时,子线程必须强制死亡
"""
主程序开始运行!
子进程开始!
主进程结束运行!
"""
四、线程的互斥锁
- 当没有设置互斥锁时,当所有线程都阻塞一秒之后,所以线程都同时拿到了100,都只减了1,所以结果会是99
from threading import Thread
import time
money = 100
def run():
global money
temp = money
time.sleep(1)
money = temp - 1
def main():
t_list = []
for i in range(20):
t = Thread(target=run)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money)
if __name__ == '__main__':
main() # 99
- 设置了互斥锁之后,虽然牺牲了效率,但保证了修改数据的安全性和可靠性
from threading import Thread, Lock
import time
money = 100
lock = Lock()
def run():
lock.acquire()
global money
temp = money
time.sleep(1)
money = temp - 1
lock.release()
def main():
t_list = []
for i in range(20):
t = Thread(target=run)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money)
if __name__ == '__main__':
main() # 80
标签:__,run,Thread,线程,print,多线程,def
From: https://www.cnblogs.com/taoyuanshi/p/18124730