进程与线程
一个进程中可以有多个进程,一个进程中可以有多个协程
CPU密集型计算(CPU—bound)
是指在I/O在很短时间就可以完成,CPU需要大量的计算和处理,特点是CPU占用率较高
eg: 压缩解压缩、加密解密、正则表达式搜索
IO密集型计算(I/O bound)
系统运作大部分是在CPU,在I/O(硬盘/内存)的读/写操作,CPU占用率较低
eg:文件处理程序、网络爬虫程序、读取数据库程序
多进程Process(multiprocessing)
优点:可以利用多核CPU
缺点:占用资源最多、可启动数目比线程少
适用于:CPU密集型计算
Process
from multiprocessing import Process
from multiprocessing import Pool
import os,time,random
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())#os.getpid()可以获取父程序的ID
# Process() 接收target 代表绑定要完成的函数,不绑定默认执行run方法,第二个元组是传入置顶函数的参数列表,
# #还可以选择性传参 name代表新建的子进程名字,还可以传入一个字典
# p = Process(target=proc_fun, args=(18,), name="哈哈1号子进程")
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start() #通过才函数来启动Process实例
p.join()#等待子程序结束后再继续往下进行,用于进程间的同步
print('Child process end.')
Pool
from multiprocessing import Pool
import os,time,random
def task(name):
print("run task %s,(%s)"%(name, os.getpid()))
start = time.time()
time.sleep(random.random()*3)
end = time.time()
print("process %s run %0.2fseconds"%(name,(end - start)))
if __name__ == '__main__':
print("Parent process %s,"%os.getpid())
p = Pool(4)#表示进程池的大小,4表示可以同时跑4个程序
for i in range(5):
p.apply_async(task,args=(i,))#在进程池中我们不能此处不能使用Process来替代
print("waitint......")
p.close()
p.join()
print("all process end")
subprocess
当我们创建了一个子进程后还想控制其输入输出就还需要用到subprocess,我们可以通过此函数来启动一个子进程并且控制其输入输出。
queue
进程间的通信通过Queue、Pipes,在父进程中创建两个子进程,一个向Queue中写数据,一个从Queue中读数据
from multiprocessing import Process,Queue
import os,time,random
def write(q):
print("start to write %s"%os.getpid())
for a in ['a','b','c']:
print("put %s into queue"%a)
q.put(a)
time.sleep(random.random())
def read(q):
print("start to read: %s" %os.getpid())
while True :
a = q.get(True)
print("get %s from queue" %a)
if __name__ =='__main__':
q = Queue()
pw = Process(target=write,args=(q,))
pr = Process(target=read, args=(q,))
pw.start()
pr.start()
pw.join()
pr.terminate()
多线程Thread(threading)
优点:相比进程,更加轻量级、占用资源少
缺点:
相比进程:多线程只能并发执行,只能利用单个CPU
相比协程:启动数目有限制,占用内存资源、有线程切换开销
适用于:IO密集型计算、同时运行的任务数目要求不多
多个任务可以有多进程完成,也可以由单个进程中的多个线程完成,且一个进程中至少有一个线程。与此同时,线程还是操作系统直接支持的单元
import threading, time,random
def loop():
print("thread %s is running" % threading.current_thread().name)#子线程
#current_thread()返回当前函数的实例
n = 0
while n < 5:
start = time.time()
n = n+1
print("kfhgk %s >> %s" %(threading.current_thread().name,n))
time.sleep(random.random())
end = time.time()
print("to run tihis process the computer have spent %0.2f" %(end - start))
print("the process is ending")
print("thread %s is running" % threading.current_thread().name)#主线程
t = threading.Thread(target=loop, name="jghdfjfg")
t.start()
t.join()
print("thread %s is ending" % threading.current_thread().name)
Lock
多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,多线程,所有变量由所有线程共享。
import threading
balance = 0;
lock = threading.Lock()
def change(m):
global balance
#执行以下两条语句需要多条语句,线程可能中断
balance = balance + m
balance = balance - m
def run_thread(m):
for i in range(2000000):#线程的调度有操作系统决定,t1、t2交替执行,当循环次数达到一定程度时输出结果便不一定准确
#加一把锁即可完美解决问题但值得注意的是当直接使用threading.Lock().acquire()会报错
#有了这把锁之后会保证在执行完当前这个线程后才会执行接下来的线程,避免了交替执行的状况。
lock.acquire()
try:
change(m)
finally:
lock.release()
t1 = threading.Thread(target=run_thread,args=(3,))
t2 = threading.Thread(target=run_thread,args=(5,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
threading.local()
在多线程的情况下,每个线程都会有多个数据,我们可以通过threading.local()方法来快捷的达到我们的目的。
import threading
NAME = threading.local()#全局变量
def process_student():
#获取当前线程的st
stn = NAME.st
print("%s %s"%(stn,threading.current_thread().name))#获取当前子进程的名字
def process_thread(ne):
#绑定当前线程的st
NAME.st = ne
process_student()
t1 = threading.Thread(target=process_thread,args=('ghkghkfgh',),name='kdkgk')
t1.start()
t1.join()
多协程Coroutine(asynico)
优点:内存开销最少、启动的数目最多
缺点:支持的库有限制、代码复杂
eg: 若欲在爬虫中采用协程则必须使用aiohttp不能使用requests库
适用于:适用于IO密集型计算、超多任务运行、但只适用于有现成库支持的场景
GIL
是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。在多核处理器上,GIL解释器也只允许同一时间执行一个线程。
在IO期间,线程会释放GIL,可以实现IO和CPU的并行运算,但是对于CPU密集型运算来说则会降低运算的速度。
在python中对于CPU密集型运算可以通过multiprocess来解决。
scoket
需要同时运行client,sever两个文件
client
import socket
client = socket.socket()
client.connect(('127.0.0.1',9999))#连接
print('client开始运行')
while True:
s = input('内容:')
#判断输入的内容,并将输入的内容传送至服务器sever
if s == 'exit':
client.send(bytes(s, encoding='utf-8'))
break
else:
client.send(bytes(s,encoding='utf-8'))
print('end')
client.close()
sever
import socket
server = socket.socket()
server.bind(('127.0.0.1',9999))#建立
server.listen(5)#监听
print('sever start on ......')
s,addr = server.accept()#接收客户端(client)内容
print('addr:{}'.format(addr))
while True:
content = s.recv(1024)#1M作为缓存 #将客户端内容以每次1M输出
#判断每次客户端内容
if str(content,encoding='utf-8') == '1':
print('wp')
continue
elif str(content,encoding='utf-8') == '2':
print('dfk')
continue
elif str(content,encoding='utf-8') == 'exit':
print('sever exit')
break
s.close()
server.close()