1.理解并发于并行
并发:两个或多个事件在同一时间间隔内发生,一个处理机处理多个事件。
如两个客户端一个服务端,服务端每个时刻只能处理一个客户端,A已连接而未发送数据,服务端便去处理B,A发送数据, 服务端停止处理B,转而去处理A。
并行:两个或多个事件在同一时刻发生,多个处理机处理多个事件。
如三个服务端三个客户端,每个服务端处理一个客户端,三个客户端每一时刻均在运行,而并行则只有一个在运行。
2.多线程实现并行
进程的概念
- 计算机程序是存储在磁盘上的文件,只有把他们加载到内存中并被操作系统调用,拥有自己的生命周期
- 进程则表示一个正在执行的程序,每个进程都拥有自己的地址空间、数据栈 以及其他用于跟踪执行的辅助数据
- 操作系统负责内存中所有进程的合理调度及执行,分配执行时间
在python中使用进程来避开耗时任务
import multiprocessing
import datetime
import time
print('mainprocess start:\t',datetime.datetime.now().time()) #主进程开启
def func():
print('subprocess start:\t',datetime.datetime.now().time())
time.sleep(3) #延时3s
print('subprocess end:\t',datetime.datetime.now().time())
p = multiprocessing.Process(target=func) #创建p进程
p.start() #启动进程
time.sleep(5) #延时5s
print('mainprocess end:\t',datetime.datetime.now().time())
mainprocess start: 17:47:55.388284
subprocess start: 17:47:55.410761
subprocess end: 17:47:58.419395
mainprocess end: 17:48:00.410063
主进程在p进程启动时与p进程分道扬镳,分别处理自己的事务。p进程延时3s后结束,主进程再延时2s结束。
此时,程序共延迟5s,而不是8s,使用进程避免了主进程的耗时。
python进程使用流程
使用multiprocessing.Process方法实例化一个进程对象
调用start()方法启动进程后,此进程便脱离主进程独立运行,执行run()内容(下面会详细分析)
创建实例时默认调用target对象,也可传递args,kwargs参数
多进程并行的必要条件
总进程数量不多于 CPU核心数量!
3.多进程实现并发
线程的概念
- 线程被称作轻量级进程,线程共属同一进程。 共享进程共享资源(数据和内存)。
- 当其他线程运行时,本线程可以被抢占(中断) 和临时挂起(也称为睡眠) — 让出资源
- 线程是操作系统能够进行运算调度的最小单位
在python中使用线程来避开阻塞任务
import threading
import datetime
import time
print('mainthread start:\t',datetime.datetime.now().time()) #主线程开启
def func():
print('subthread start:\t',datetime.datetime.now().time())
time.sleep(3) #延时3s
print('subthread end:\t',datetime.datetime.now().time())
t = threading.Thread(target=func) #创建t线程
t.start() #启动进程
time.sleep(5) #延时5s
print('mainthread end:\t',datetime.datetime.now().time())
mainthread start: 18:54:24.439408
subthread start: 18:54:24.443931
subthread end: 18:54:27.452842
mainthread end: 18:54:29.445461
线程使用流程
线程VS进程
- 稳定性:进程具有独立的地址空间,一个进程崩溃后,不会对其它进程产生影响 线程共享进程地址空间,一个线程崩溃后,整个进程就崩溃了
- 创建开销:创建进程操作系统是要分配内存空间和一些其他资源的。 开销很大 创建线程操作系统不需要再单独分配资源,开销较小
- 切换开销:不同进程直接是独立的, 切换需要耗费较大资源 线程共享进程地址空间, 切换开销小
GIL锁(线程锁)
Python在设计的时候,还没有多核处理器的概念。 因此,为了设计方便与线程安全,直接设计了一个锁。 这个锁要求,任何进程中,一次只能有一个线程在执行。
因此,并不能为多个线程分配多个CPU。 所以Python中的线程只能实现并发, 而不能实现真正的并行。
但是Python3中的GIL锁有一个很棒的设计, 在遇到阻塞(不是耗时)的时候,会自动切换线程。
GIL锁的新认知
GIL锁会使线程遇到阻塞就自动切换。 因此我们可以利用这种机制来,有效的避开阻塞,充分利用CPU
4.使用多进程与多线程来实现并发服务器
关键点一: 多进程是并行执行,相当于分别独立得处理各个请求。
关键点二: 多线程,虽然不能并行运行,但是可以通过避开阻塞切换线程来实现并发的效果,并且不浪费cpu。
多进程实现并发服务器
import multiprocessing #进程
import threading #线程
import socket
sock=socket.socket()
sock.bind(('0.0.0.0',8088))
sock.listen(5)
def worker(conn):
while True:
try:
data = conn.recv(1024)
if data:
print(data)
conn.send(data)
else:
conn.close()
break
except Exception as e:
conn.close()
break
while True:
conn,addr=sock.accept()
#每生成一个对等连接套接字,便生成一个进程,并交给这个进程去处理
p= threading.Thread(target=worker,args=(conn,))
p.start() #开启进程
多线程实现并发服务器
import multiprocessing #进程
import threading #线程
import socket
sock=socket.socket()
sock.bind(('0.0.0.0',8088))
sock.listen(5)
def worker(conn):
while True:
try:
data = conn.recv(1024)
if data:
print(data)
conn.send(data)
else:
conn.close()
break
except Exception as e:
conn.close()
break
while True:
conn,addr=sock.accept()
#每生成一个对等连接套接字,便生成一个线程,并交给这个线程去处理
t= multiprocessing.Process(target=connection,args=(conn,))
t.start() #开启线程