线程与进程
-
进程简单理解就是你的设备打开的应用程序,你可以同时打开多个程序,这些程序可以同时运行
-
线程简单理解就是应用程序中执行的指令,我们一般情况下都是单线程,但是也可以使用多个线程使程序同时执行多条指令
-
线程是包含在进程里的
多线程
在单线程时
def func_1():
for i in range(5):
print("func_1",i)
def func_2():
for i in range(5):
print("func_2",i)
if __name__ == '__main__':
func_1()
func_2()
结果如下
可以看到在func_1执行完之后才执行func_2
使用Thread
创建线程, target
为线程要执行的函数,args
为一个元组,将被传递给target对应的函数
循环次数稍微多一点,线程启动有一个时间差,如果次数太少,可能 t1 执行完毕, t2 才启动
from threading import Thread
def func(name):
for i in range(100):
print(name,i)
if __name__ == '__main__':
t1=Thread(target=func,args=("_t1_",))
t2=Thread(target=func,args=("_t2_",))
t1.start()
t2.start()
可以看到 t1 和 t2 的打印结果是混在一起的
可以通过继承Thread重写自定义线程类,这种方式在传参上更加灵活,可以在构造函数中去做一些操作
from threading import Thread
class MyThread(Thread):
def run(self):
for i in range(10):
print(i)
mythread=MyThread()
MyThread.start()
注意,自定义线程类必须重写 run()方法,启动对象时要使用.start()
方法,才能使用多线程,直接使用run()方法只是普通的调用了该方法,并没有创建一个线程
多进程
开辟进程消耗的资源要比开辟线程更多,所以我们通常使用多线程而不是多进程
使用多进程的方法与多线程基本一致,只是用到的库不同,使用:
from multiprocessing import Process
替代 Thread 即可
进程池和线程池
在使用爬虫爬取多页数据时,如果我们通过for循环一个页面一个页面去访问,那么需要的时间将会非常长,所以我们可以使用多线程和多进程来同时爬取多个页面
那么,此时创建的多个线程和进程应该如何管理呢,如何分配任务呢?我们可以使用线程池和进程池来方便的进行线程和进程的创建和调度
我们可以使用线程池和进程池来批量创建多个线程和进程,并且直接向线程池和进程池提交任务,线程池和进程池会自动的分配哪个线程和进程执行哪个任务
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def func(name):
for i in range(100):
print(name+"第"+str(i)+"\n",end="")
# 线程池
with ThreadPoolExecutor(10) as T:
for i in range(100):
T.submit(func,name=f"任务{i}__")
# 进程池
with ProcessPoolExecutor(10) as T:
for i in range(100):
T.submit(func,name=f"任务{i}__")
这样就非常简单的创建了10个线程并自动的为它们分配了任务
这样做有什么用吗?直接循环创建线程不行吗?
由于任务内容不同,或者网络波动等因素的影响,任务结束的顺序与任务开始的顺序并不相同,我们如果要判定哪些线程已经完成了任务然后给他们安排新任务就会比较麻烦
2023.3.23