python进阶8-多线程和多进程
一、线程&进程
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。进程是很多资源的集合。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。线程是最小的执行单元,而进程由至少一个线程组成。
我们在做事情的时候,一个人做是比较慢的,如果多个人一起来做的话,就比较快了,程序也是一样的,我们想运行的速度快一点的话,就得使用多进程,或者多线程,在python里面,多线程被很多人诟病,为什么呢,因为Python的解释器使用了GIL的一个叫全局解释器锁,它不能利用多核CPU,只能运行在一个cpu上面,但是你在运行程序的时候,看起来好像还是在一起运行的,是因为操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。这个叫做上下文切换。
二、多线程
python中的多线程使用theading模块
下面是一个简单多线程
import threading
import time
def sayhi(num): # 定义每个线程要运行的函数
print(<span class="hljs-string">"running on number:%s"</span> % num)
time.sleep(<span class="hljs-number">3</span>)
print(<span class="hljs-string">"running on number:%s"</span> % num)
time.sleep(<span class="hljs-number">3</span>)
if name == 'main':
t1 = threading.Thread(target=sayhi, args=(1,)) # 生成一个线程实例
t2 = threading.Thread(target=sayhi, args=(2,)) # 生成另一个线程实例
t1.start() # 启动线程
t2.start() # 启动另一个线程
下面是另一种启动多线程的方式,继承式
import threading
import time
class MyThread(threading.Thread):
def init(self, num):
threading.Thread.init(self)
self.num = num
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span> <span class="hljs-comment"># 定义每个线程要运行的函数</span>
print(<span class="hljs-string">"running on number:%s"</span> % self.num)
time.sleep(<span class="hljs-number">3</span>)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span> <span class="hljs-comment"># 定义每个线程要运行的函数</span>
print(<span class="hljs-string">"running on number:%s"</span> % self.num)
time.sleep(<span class="hljs-number">3</span>)
if name == 'main':
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
这两种方式没有什么区别,两种写法而已,我个人喜欢用第一种,更简单一些。
线程等待
多线程在运行的时候,每个线程都是独立运行的,不受其他的线程干扰,如果想在哪个线程运行完之后,再做其他操作的话,就得等待它完成,那怎么等待呢,使用join,等待线程结束
import threading
import time
def run():
print('qqq')
time.sleep(1)
print('done!')
lis = []
for i in range(5):
t = threading.Thread(target=run)
lis.append(t)
t.start()
for t in lis:
t.join()
print('over')
或者使用继承式
import threading
import time
class MyThread(threading.Thread):
def init(self,num):
threading.Thread.init(self)
self.num = num
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span>
print(<span class="hljs-string">"running on number:%s"</span> % self.num)
time.sleep(<span class="hljs-number">3</span>)
print(<span class="hljs-string">'time end:%s'</span> % self.num)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span>
print(<span class="hljs-string">"running on number:%s"</span> % self.num)
time.sleep(<span class="hljs-number">3</span>)
print(<span class="hljs-string">'time end:%s'</span> % self.num)
if name == 'main':
lis = []
for i in range(5):
t = MyThread(i)
lis.append(t)
t.start()
for t in lis:
t.join()
print('over')
如果不加join,线程执行后,会立即继续执行print('over') 然后继续等线程执行完毕;加上join后,Python会等线程行结束后,再执行print('over')
守护线程
有一种线程,它是在后台运行的,它的任务是为其他线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,又称为“守护线程”或“精灵线程”。Python 解释器的垃圾回收线程就是典型的后台线程。
后台线程有一个特征,如果所有的前台线程都死亡了,那么后台线程会自动死亡。
简单来说,什么是守护线程呢,就相当于你是一个国王(非守护线程),然后你有很多仆人(守护线程),这些仆人都是为你服务的,一但你死了,那么你的仆人都给你陪葬。
import threading
# 定义后台线程的线程执行体与普通线程没有任何区别
def action(max):
for i in range(max):
print(threading.current_thread().name + " " + str(i))
t = threading.Thread(target=action, args=(100,), name='后台线程')
# 将此线程设置成后台线程
# 也可在创建Thread对象时通过daemon参数将其设为后台线程
t.daemon = True
# 启动后台线程
t.start()
for i in range(10):
print(threading.current_thread().name + " " + str(i))
# -----程序执行到此处,前台线程(主线程)结束------
# 后台线程也应该随之结束
也可在创建Thread对象时通过daemon参数将其设为后台线程
import threading
# 定义后台线程的线程执行体与普通线程没有任何区别
class MyThread(threading.Thread):
def init(self,max,daemon=False):
threading.Thread.init(self)
self.max = max
self.daemon = daemon
def run(self):
for i in range(self.max):
print(threading.current_thread().name + " " + str(i))
# 将此线程设置成后台线程
t = MyThread(100,daemon=True)
# 启动后台线程
t.start()
for i in range(10):
print(threading.current_thread().name + " " + str(i))
# -----程序执行到此处,前台线程(主线程)结束------
# 后台线程也应该随之结束
注意,当前台线程死亡后,Python 解释器会通知后台线程死亡,但是从它接收指令到做出响应需要一定的时间。如果要将某个线程设置为后台线程,则必须在该线程启动之前进行设置。也就是说,将 daemon 属性设为 True,必须在 start() 方法调用之前进行,否则会引发 RuntimeError 异常。
线程锁
线程锁就是,很多线程一起在操作一个数据的时候,可能会有问题,就要把这个数据加个锁,同一时间只能有一个线程操作这个数据。
import threading
from threading import Lock
num = 0
lock = Lock()#申请一把锁
def run():
global num
lock.acquire()#加锁
num+=1
lock.release()#解锁
lis = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">5</span>):
t = threading.Thread(target=run)
t.start()
lis.append(t)
<span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> lis:
t.join()
print(<span class="hljs-string">'over'</span>,num)
lis = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">5</span>):
t = threading.Thread(target=run)
t.start()
lis.append(t)
<span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> lis:
t.join()
print(<span class="hljs-string">'over'</span>,num)
下面来个简单的爬虫,看下多线程的效果
import threading
import requests,time
urls ={
"baidu":'http://www.baidu.com',
"blog":'http://www.nnzhp.cn',
"besttest":'http://www.besttest.cn',
"taobao":"http://www.taobao.com",
"jd":"http://www.jd.com",
}
def run(name,url):
res = requests.get(url)
with open(name+'.html','w',encoding=res.encoding) as fw:
fw.write(res.text)
start_time = time.time()
lis = []
<span class="hljs-keyword">for</span> url <span class="hljs-keyword">in</span> urls:
t = threading.Thread(target=run,args=(url,urls[url]))
t.start()
lis.append(t)
<span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> lis:
t.join()
end_time = time.time()
print(<span class="hljs-string">'run time is %s'</span>%(end_time-start_time))
<span class="hljs-comment">#下面是单线程的执行时间</span>
<span class="hljs-comment"># start_time = time.time()</span>
<span class="hljs-comment"># for url in urls:</span>
<span class="hljs-comment"># run(url,urls[url])</span>
<span class="hljs-comment"># end_time = time.time()</span>
<span class="hljs-comment"># print('run time is %s'%(end_time-start_time))</span>
start_time = time.time()
lis = []
<span class="hljs-keyword">for</span> url <span class="hljs-keyword">in</span> urls:
t = threading.Thread(target=run,args=(url,urls[url]))
t.start()
lis.append(t)
<span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> lis:
t.join()
end_time = time.time()
print(<span class="hljs-string">'run time is %s'</span>%(end_time-start_time))
<span class="hljs-comment">#下面是单线程的执行时间</span>
<span class="hljs-comment"># start_time = time.time()</span>
<span class="hljs-comment"># for url in urls:</span>
<span class="hljs-comment"># run(url,urls[url])</span>
<span class="hljs-comment"># end_time = time.time()</span>
<span class="hljs-comment"># print('run time is %s'%(end_time-start_time))</span>
三、多进程
上面说了Python里面的多线程,是不能利用多核CPU的,如果想利用多核CPU的话,就得使用多进程,python中多进程使用multiprocessing模块。
from multiprocessing import Process
import time
def f(name):
time.sleep(2)
print('hello', name)
p = Process(target=f, args=('niu',))
p.start()
p.join()
转载自https://www.cnblogs.com/feng0815/p/7953788.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏