关于进程与线程的概念,这里简单介绍下。
一个进程是一个独立的执行环境,包括代码、数据和系统资源等,每个进程都有自己的内存空间、文件描述符、环境变量等。
而线程存在于进程中,共享进程内的内存和资源。
至于多进程与多线程,多进程可以充分利用计算机的多核 CPU,适用于 CPU 密集型的任务,,比如进行大量计算操作
而多线程则适用于涉及到大量的 IO 操作的任务,比如网络请求,文件读写等,在 Python 中有一个 GIL 的概念,它的全称是 Global Interpreter Lock,为全局解释器锁。
GIL 的存在是为了使同一时刻只有一个线程在运行 Python 代码,保护解释器的内部数据避免收到并发访问的影响。
所以 Python 中的多线程操作实际上是在多个线程中进行切换,以此来实现想要的并发效果。
前面介绍了 Python 中多线程的操作适用于 IO 密集型的任务,所以这里以访问某个接口为例介绍一下多线程的使用。
那个接口我们这里用 Flask 创建一个服务器,其内容如下:
# app/__init__.py
from flask import Flask
import time
def create_app():
app = Flask(__name__)
@app.route("/test/<int:delay>")
def test(delay):
time.sleep(delay)
return str(time.time())
return app
这个接口通过 delay 参数可以指定接口的休眠时间返回,比如 /test/4
,那么接口响应时间大约会是 4 秒。
在 Python 中,用到多线程的模块是 threading
模块,以下是一个使用示例:
import threading
import time
import requests
def get_response(url):
response = requests.get(url)
print(response.content)
def test_multi_threading():
url = "http://192.168.1.6:5000/test/2"
threads = []
for i in range(20):
threads.append(threading.Thread(target=get_response, args=(url,)))
for t in threads:
t.start()
for t in threads:
t.join()
def test_single():
url = "http://192.168.1.6:5000/test/2"
for i in range(5):
get_response(url)
if __name__ == "__main__":
start_time = time.time()
test_multi_threading()
print("运行耗时:", time.time() - start_time)
start_time = time.time()
test_single()
print("运行耗时:", time.time() - start_time)
在这里我们可以比对单个线程执行五次,需要的时间大约是 10 秒,而使用多线程的方式虽然调用了 20 次接口,但是耗时大约只有 2 秒,这就是多线程在 IO 密集型的情况下的好处。
接下来具体介绍下多线程的使用方法:
def test_multi_threading():
url = "http://192.168.1.6:5000/test/2"
threads = []
for i in range(20):
threads.append(threading.Thread(target=get_response, args=(url,)))
for t in threads:
t.start()
for t in threads:
t.join()
在这里,我们通过 threading.Thread()
的方式创建一个线程,然后通过 .start()
方法开始线程活动。
接着通过 join()
方法阻塞调用这个方法的线程,在这里也就是主线程,等待 t 线程完成后再执行主线程后面的操作。
如果我们尝试注释掉 t.join()
这两行,那么主线程就会不等待 t 线程直接往后面执行,造成我们后面在主函数里计算的时间不准确。
可以根据这个参数设置线程是否为守护线程,所有线程创建的时候默认都不是守护线程,