
1.进程与多进程
1.1.什么是进程
- 进程就是程序执行的载体
- 什么叫多任务?
- 多任务就是操作系统可以同时运行多个任务。比如你一边在用浏览器学习,还一边在听音乐,,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。
- 什么是进程?
- 对于操作系统来说,一个任务就是一个进程,比如打开一个浏览器就是启动一个浏览器进程,打开一个word就启动了一个word进程,打开两个记事本就启动了两个记事本进程。
- 怎样的任务算一个进程?
- 当一个任务被开启后,操作系统会分配它所需的系统资源,包括内存,I/O和CPU等,如果系统资源不够,则会出现系统崩溃,这样的任务可称为进程。
- python中如何创建进程?
- 使用模块:multiprocessing
- 创建方法: multiprocessing.Process(...)
1.2.进程在生活中的应用
- 我们打开的每个软件、游戏、执行的每一个python脚本都是启动一个进程
- 软件(游戏,脚本)==进程
1.3.进程的口粮
每一个进程像人一样需要吃饭,他的粮食就是:cpu和内存
1.4.多进程
可以启动多个进程,他们之间互不干扰,执行自己的业务逻辑
1.5.多进程的执行方式
2.线程与多线程
2.1.什么是线程
- 先有进程再有线程,进程吸收足够的资源(CPU、内存)然后交给线程,线程是真正执行逻辑的角色。
- 线程是操作系统最小的执行单元,进程至少由一个线程组成。如何调度进程和线程,完全有操作系统决定,程序自己不能决定什么时候执行,执行多长时间。有些进程还不止同时干一件事,比如微信,它可以同时进行语音、发文字、浏览信息等事情。
- 怎样的任务算一个线程?
- 进程被运行后算一个线程,进程是不运行的,线程才会运行,而一个进程有多个线程就涉及到进程有多少可以被cpu单独调用的模块,这个调用的模块可以通过手动创建线程来建立。
- 在python中如何创建线程?
- 使用模块:threading
- 创建方法:threading.Thread(...)
2.2.线程与进程的关系
进程提供线程执行程序的前置要求,线程在重组的资源配备下,去执行程序
2.3.多线程
- 举例说明:开启一个浏览器进程后,从浏览器(主线程)中创建出多个线程来开启多个页面即一个浏览器打开多个tab页,这些tab页就是浏览器进程的多线程
- 初级认知:多线程会比多进程更加节省资源
2.4.多线程的执行方式
- 并行:在多个cpu内核上同时执行多个进程
- 并发:在多个cpu时间片上同时执行多个线程
- 线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位。线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行,进程之间不能共享内存,但线程之间可以共享内存
3.多进程的创建
3.1.进程的创建模块-multiprocessing
1 # coding:utf-8 2 3 import time 4 import os 5 def work_a(): 6 for i in range(3): 7 print(i,"a",os.getpid()) #os.getpid()获取进程id 8 time.sleep(1) 9 10 def work_b():11 for i in range(3):12 print(i,"b",os.getpid())13 time.sleep(1)14 15 if __name__=='__main__':16 start=time.time()17 work_a()18 work_b()19 end=time.time()-start20 print(end)21 print('parent pid is %s' % os.getpid())22 '''23 0 a 3643624 1 a 3643625 2 a 3643626 0 b 3643627 1 b 3643628 2 b 3643629 6.00243711471557630 parent pid is 36436 所有的进程id跟主进程一致,都是3643631 '''1 # coding:utf-8 2 3 import time 4 import os 5 import multiprocessing 6 7 #创建进程 8 9 def work_a():10 for i in range(3):11 print(i,"a",os.getpid()) #os.getpid()获取进程id12 time.sleep(1)13 14 def work_b():15 for i in range(3):16 print(i,"b",os.getpid())17 time.sleep(1)18 19 if __name__=='__main__':20 start=time.time()21 a_p=multiprocessing.Process(target=work_a) #将work_a()方法单独作为一个进程执行,由于work_a()方法没有参数,因此args为None,也可以不填22 a_p.start() #启动work_a()进程23 work_b()24 end=time.time()-start25 print(end)26 print('parent pid is %s' % os.getpid())27 '''28 0 b 3519629 0 a 2464030 1 b 3519631 1 a 2464032 2 b 3519633 2 a 2464034 3.012757539749145535 parent pid is 3519636 work_a()单独用了一个进程运行,work_b()依旧与主线程用的同一个进程37 '''1 # coding:utf-8 2 3 import time 4 import os 5 import multiprocessing 6 7 #多个进程执行 8 9 def work_a():10 for i in range(3):11 print(i,"a",os.getpid()) #os.getpid()获取进程id12 time.sleep(1)13 14 def work_b():15 for i in range(3):16 print(i,"b",os.getpid())17 time.sleep(1)18 19 if __name__=='__main__':20 start=time.time() #主进程21 a_p=multiprocessing.Process(target=work_a) #子进程122 b_p=multiprocessing.Process(target=work_b) #子进程223 end=time.time()-start #主进程24 print(end) #主进程25 print('parent pid is %s' % os.getpid()) #主进程26 for i in (a_p,b_p):27 i.start()28 '''29 0.030 parent pid is 1969231 0 a 3540832 0 b 3400033 1 a 3540834 1 b 3400035 2 a 3540836 2 b 3400037 '''1 # coding:utf-8 2 3 import time 4 import os 5 import multiprocessing 6 7 #join方法使用 8 9 def work_a():10 for i in range(3):11 print(i,"a",os.getpid()) #os.getpid()获取进程id12 time.sleep(1)13 14 def work_b():15 for i in range(3):16 print(i,"b",os.getpid())17 time.sleep(1)18 19 if __name__=='__main__':20 start=time.time() #主进程21 a_p=multiprocessing.Process(target=work_a) #子进程122 b_p=multiprocessing.Process(target=work_b) #子进程223 for i in (a_p,b_p):24 i.start()25 i.join()26 end=time.time()-start #主进程27 print(end) #主进程28 print('parent pid is %s' % os.getpid()) #主进程29 '''30 0 a 3581631 1 a 3581632 2 a 3581633 0 b 3517234 1 b 3517235 2 b 3517236 6.11802530288696337 parent pid is 1462438 '''1 # coding:utf-8 2 3 import time 4 import os 5 import multiprocessing 6 7 #kill方法使用 8 9 def work_a():10 for i in range(3):11 print(i,"a",os.getpid()) #os.getpid()获取进程id12 time.sleep(1)13 14 def work_b():15 for i in range(3):16 print(i,"b",os.getpid())17 time.sleep(1)18 19 if __name__=='__main__':20 start=time.time() #主进程21 a_p=multiprocessing.Process(target=work_a) #子进程122 b_p=multiprocessing.Process(target=work_b) #子进程223 for i in (a_p,b_p):24 i.start()25 if i==b_p:26 i.kill()27 end=time.time()-start #主进程28 print(end) #主进程29 print('parent pid is %s' % os.getpid()) #主进程30 '''31 0.01600098609924316432 parent pid is 3526833 0 a 3458434 1 a 3458435 2 a 3458436 '''3.2.进程的问题
通过进程模块执行的函数无法获取返回值
多个进程同时修改文件,可能会出现错误
进程太多会导致资源不足
3.3.多进程总结
一、当多个进程运行时,可能会出现的问题及解决方案
- 通过进程模块执行的函数无法获取返回值--进程间如何通信:通过队列
- 多个进程同时修改文件可能会出现错误--进程间如何避免资源抢占:创建进程锁
- 进程数量太多可能会造成资源不足 甚至死机等情况--如何避免创建进程数量过多:创建进程池
二、什么是队列
队列是一种数据存储结构,它的数据存储特点类似于排队,先进入队列的会先出来,后进入队列的 后出来,因此它的数据只要通过put()放入,get()取出即可,不需要安排取哪些数据进程的数据可放 入队列,哪些进程需要,从队列中通过取出,即可使用。
三、如何创建队列
- 使用的模块: queue
- 创建的方法: queue. Queue(…)
4.进程池与进程锁
4.1.什么是进程池
4.2.进程池的创建
进程池的join函数一般伴随着close函数
1 # coding:utf-8 2 3 import os 4 import time 5 import multiprocessing 6 7 def work(count): 8 print(count,os.getpid()) 9 time.sleep(5)10 11 12 if __name__=='__main__':13 pool=multiprocessing.Pool(5) #创建进程池,里面有5个进程14 for i in range(20):15 pool.apply_async(func=work,args=(i,)) #进程池和进程对象process创建对象的时候里面的参数是元组类型的,单个参数时,结尾使用,逗号标明是元组类型16 17 #进程池属于主进程里面的子进程,主进程结束,pycharm就退出了18 #因此需要使用等待或者pool.close等方法19 pool.close()20 pool.join()21 #为什么是先关闭进程池再等待呢?22 #pool.close()是关闭进程池,使其不在接受新的任务。防止任何更多的任务被提交到池中。 一旦完成所有任务,工作进程将退出。需要在join之前调用;23 #可以这样理解,这里的进程池就相当于一辆大巴车,而close相当于大巴车不准上客,join大巴车上的乘客(子进程)都下车之后,大巴车就停运了24 '''25 0 1009226 1 1970427 2 3616428 3 3721229 4 3462830 5 1009231 6 1970432 7 3616433 8 3721234 9 3462835 10 1009236 11 1970437 12 3616438 13 3721239 14 3462840 15 1009241 16 1970442 17 3616443 18 3721244 19 3462845 '''1 # coding:utf-8 2 3 import os 4 import time 5 import multiprocessing 6 7 #通过进程池可以获取返回值 8 def work(count): 9 print(count,os.getpid())10 time.sleep(5)11 return 'result is %s ,pid is %s' % (count,os.getpid())12 13 14 if __name__=='__main__':15 pool=multiprocessing.Pool(5) #创建进程池,里面有5个进程16 results=[]17 for i in range(20):18 result=pool.apply_async(func=work,args=(i,)) #进程池和进程对象process创建对象的时候里面的参数是元组类型的,单个参数时,结尾使用,逗号标明是元组类型19 results.append(result)20 21 for res in results:22 print(res.get())23 24 '''25 0 1009226 1 1970427 2 3616428 3 3721229 4 3462830 5 1009231 6 1970432 7 3616433 8 3721234 9 3462835 10 1009236 11 1970437 12 3616438 13 3721239 14 3462840 15 1009241 16 1970442 17 3616443 18 3721244 19 3462845 '''4.3.进程锁
4.4.进程锁的用法
1 # coding:utf-8 2 3 import os 4 import time 5 import multiprocessing 6 7 #进程锁 8 def work(count,lock): 9 lock.acquire()10 print(count,os.getpid())11 time.sleep(1)12 lock.release()13 return 'result is %s ,pid is %s' % (count,os.getpid())14 15 16 if __name__=='__main__':17 pool=multiprocessing.Pool(5)18 manger=multiprocessing.Manager()19 lock = manger.Lock()20 results=[]21 for i in range(20):22 result=pool.apply_async(func=work,args=(i,lock))23 24 pool.close()25 pool.join()
5.进程间的通信
5.1.什么是进程的通信
使用队列进行进程之间的通信
5.2.进程通信的方法
1 # coding:utf-8 2 import json 3 import multiprocessing 4 5 class Work(object): 6 def __init__(self,q): 7 self.q=q 8 9 def send(self,message):10 if not isinstance(message,str):11 message=json.dumps(message)12 self.q.put(message)13 14 def receive(self):15 while 1:16 result=self.q.get()17 try:18 res=json.loads(result)19 except:20 res = result21 print('recv is %s' % res)22 23 if __name__=='__main__':24 q=multiprocessing.Queue()25 work=Work(q)26 send=multiprocessing.Process(target=work.send,args=({'name':'zhangsan'},))27 recv=multiprocessing.Process(target=work.receive)28 send.start()29 recv.start()30 31 send.join()32 recv.terminate() #强行终止1 # coding:utf-8 2 import json 3 import multiprocessing 4 import time 5 6 7 class Work(object): 8 def __init__(self,q): 9 self.q=q10 11 def send(self,message):12 if not isinstance(message,str):13 message=json.dumps(message)14 self.q.put(message)15 16 def send_all(self):17 for i in range(10):18 self.q.put(i)19 time.sleep(1)20 21 def receive(self):22 while 1:23 result=self.q.get()24 try:25 res=json.loads(result)26 except:27 res = result28 print('recv is %s' % res)29 30 if __name__=='__main__':31 q=multiprocessing.Queue()32 work=Work(q)33 send=multiprocessing.Process(target=work.send,args=({'name':'zhangsan'},))34 recv=multiprocessing.Process(target=work.receive)35 send_all_p=multiprocessing.Process(target=work.send_all)36 send.start()37 recv.start()38 send_all_p.start()39 40 send_all_p.join()41 recv.terminate() #强行终止
6.线程的创建
6.1.线程的创建
1 # coding:utf-8 2 3 import random 4 import time 5 6 lists=['python','django','tornado','flask','bs5','requests','uvloop'] 7 8 new_lists=[] 9 10 def work():11 if len(lists) == 0:12 return13 data=random.choice(lists)14 lists.remove(data)15 new_data='%s_new' % data16 new_lists.append(new_data)17 time.sleep(1)18 19 if __name__=='__main__':20 start=time.time()21 for i in range(len(lists)):22 work()23 print('old list',lists)24 print('new list',new_lists)25 print('time is %s' % (time.time()-start))26 '''27 old list []28 new list ['uvloop_new', 'requests_new', 'django_new', 'bs5_new', 'flask_new', 'python_new', 'tornado_new']29 time is 7.00307655334472730 '''1 # coding:utf-8 2 3 import random 4 import time 5 import threading 6 lists=['python','django','tornado','flask','bs5','requests','uvloop'] 7 8 new_lists=[] 9 10 def work():11 if len(lists) == 0:12 return13 data=random.choice(lists)14 lists.remove(data)15 new_data='%s_new' % data16 new_lists.append(new_data)17 time.sleep(1)18 19 if __name__=='__main__':20 start=time.time()21 t_list=[]22 for i in range(len(lists)):23 t=threading.Thread(target=work)24 t_list.append(t)25 t.start()26 27 for t in t_list:28 t.join()29 print('old list',lists)30 print('new list',new_lists)31 print('time is %s' % (time.time()-start))32 '''33 old list []34 new list ['requests_new', 'tornado_new', 'flask_new', 'bs5_new', 'python_new', 'django_new', 'uvloop_new']35 time is 1.001986265182495136 '''6.2.线程的问题
6.3.线程创建总结
一、当多个线程运行时,可能会出现的问题及解决方案:
- 通过线程执行的函数无法获取返回值——线程间如何通信:通过队列
- 多个线程同时修改文件可能造成数据错错乱——线程间如何避免资源抢占:创建线程锁
- 线程数量太多可能会造成资源不足,甚至死机等情况——如何避免创建线程数量过多:创建线程池
二、通过队列通信来解决
三、创建线程锁:
在线程代码中需要加上锁的地方写上加锁代码,要释放锁的地方写解锁代码即可
- 使用模块:threading
- 加锁:threading.Lock().acquire()
- 解锁:threading.Lock().release()
四、创建线程池:
首先写出创建线程池的方法,之后往线程池中放入线程即可
- 使用模块:concurrent.futures
- 创建方法:concurrent.futures.ThreadPoolExecutor()
7.线程池
线程池的创建与使用方法,线程池不像进程池需要close(),而后join()
1 # coding:utf-8 2 import time 3 from concurrent.futures import ThreadPoolExecutor 4 5 def work(i): 6 print(i) 7 time.sleep(1) 8 9 if __name__=="__main__":10 t=ThreadPoolExecutor(2)11 for i in range(10):12 t.submit(work,i)13 14 '''15 016 117 2318 19 420 521 622 723 824 925 '''1 # coding:utf-8 2 import threading 3 import time 4 from concurrent.futures import ThreadPoolExecutor 5 6 #线程锁和进程锁的区别:进程锁需要将锁传到方法中,而线程锁只需要定义一个全局锁,直接调用即可 7 lock=threading.Lock() 8 9 def work(i):10 lock.acquire()11 print(i)12 time.sleep(1)13 lock.release()14 15 if __name__=="__main__":16 t=ThreadPoolExecutor(2)17 for i in range(10):18 t.submit(work,i)19 20 '''21 022 123 224 325 426 527 628 729 830 931 '''1 # coding:utf-8 2 import os 3 import threading 4 import time 5 from concurrent.futures import ThreadPoolExecutor 6 7 #线程池和进程池一样可以获得线程的返回值,多线程的os.getpid和当前进程的一致,用的同一个,因为线程池就是在当前进程下执行的 8 9 def work(i):10 time.sleep(1)11 return 'result %s' % i12 13 if __name__=="__main__":14 print(os.getpid()) #5776415 t=ThreadPoolExecutor(2)16 list_1=[]17 for i in range(10):18 result=t.submit(work,i)19 list_1.append(result)20 print(list_1,type(list_1)) #[<Future at 0x2bfbe0bc748 state=running>, <Future at 0x2bfbe2fbd08 state=running>, <Future at 0x2bfbe301808 state=pending>, <Future at 0x2bfbe301908 state=pending>, <Future at 0x2bfbe3019c8 state=pending>, <Future at 0x2bfbe301b48 state=pending>, <Future at 0x2bfbe301cc8 state=pending>, <Future at 0x2bfbe301e88 state=pending>, <Future at 0x2bfbe30c048 state=pending>, <Future at 0x2bfbe301b08 state=pending>] <class 'list'>21 22 for i in list_1:23 print(i.result())24 '''25 result 026 result 127 result 228 result 329 result 430 result 531 result 632 result 733 result 834 result 935 '''
8.全局锁
一、什么是全局锁:
GIL是全局解释器锁,这个GIL并不是python的特性,他是在Cpython解释器里引入的一个概念,而 在其他的语言编写的解释器里就没有这个GIL
二、全局锁是主要的作用 :
因为多线程的编程方式,使得线程之间数据的一致性和状态同步难以把控,为了解决数据不能同步 的问题,设计了GIL全局解释器锁。
三、全局锁是如何发挥作用的 :
在Cpython解释器中,当python代码有一个线程开始访问解释器的时候,GIL会把这个线程给锁上, 此时此刻其他的线程只能干等着,无法对解释器的资源进行访问,需要等这个线程分配的时间到 了,这个线程把GIL释放掉,另外的线程才开始跑起来,其实这无疑也是一个单线程。这类似于给 线程加锁 threading.Lock().acquire() ,解锁 threading.Lock().release() 一样。
python有全局锁gil,多线程只能在单一cpu工作。可以用多进程+多线程配合使用
9.异步
9.1.什么是异步与异步的好处
9.2.异步与多线程多进程
9.3.async、await与asyncio模块的使用
1 # coding:utf-8 2 import random 3 import time 4 5 6 def a(): 7 for i in range(10): 8 print(i,'a') 9 time.sleep(random.random()*2)10 return 'a function'11 12 def b():13 for i in range(10):14 print(i,'b')15 time.sleep(random.random()*2)16 return 'b function'17 18 19 if __name__=='__main__':20 start=time.time()21 a()22 b()23 print(time.time()-start)24 '''25 0 a26 1 a27 2 a28 3 a29 4 a30 5 a31 6 a32 7 a33 8 a34 9 a35 0 b36 1 b37 2 b38 3 b39 4 b40 5 b41 6 b42 7 b43 8 b44 9 b45 19.6855709552764946 '''1 # coding:utf-8 2 import asyncio 3 import random 4 import time 5 6 7 async def a(): 8 for i in range(10): 9 print(i,'a')10 await asyncio.sleep(random.random()*2)11 return 'a function'12 13 async def b():14 for i in range(10):15 print(i,'b')16 await asyncio.sleep(random.random()*2)17 return 'b function'18 19 async def main():20 result=await asyncio.gather(21 a(),22 b()23 )24 print(result)25 26 if __name__=='__main__':27 start=time.time()28 asyncio.run(main())29 print(time.time()-start)30 '''31 0 a32 0 b33 1 b34 2 b35 1 a36 3 b37 2 a38 3 a39 4 b40 4 a41 5 a42 5 b43 6 a44 7 a45 6 b46 8 a47 7 b48 9 a49 8 b50 9 b51 ['a function', 'b function']52 10.61219024658203153 '''1 # coding:utf-8 2 import asyncio 3 import random 4 import time 5 6 7 async def a(): 8 for i in range(10): 9 print(i,'a')10 await asyncio.sleep(random.random()*2)11 return 'a function'12 13 async def b():14 for i in range(10):15 print(i,'b')16 await asyncio.sleep(random.random()*2)17 return 'b function'18 19 async def main():20 result=await asyncio.gather(21 a(),22 b()23 )24 print(result)25 26 if __name__=='__main__':27 start=time.time()28 # asyncio.run(main())29 asyncio.run(b())30 print(time.time()-start)31 '''32 0 b33 1 b34 2 b35 3 b36 4 b37 5 b38 6 b39 7 b40 8 b41 9 b42 6.93254208564758343 '''1 # coding:utf-8 2 import asyncio 3 import os 4 import random 5 import time 6 7 8 async def a(): 9 for i in range(10):10 print(i,'a',os.getpid())11 await asyncio.sleep(random.random()*2)12 return 'a function'13 14 async def b():15 for i in range(10):16 print(i,'b',os.getpid())17 await asyncio.sleep(random.random()*2)18 return 'b function'19 20 async def main():21 result=await asyncio.gather(22 a(),23 b()24 )25 print(result)26 27 if __name__=='__main__':28 start=time.time()29 asyncio.run(main())30 print(time.time()-start)31 print('parent is %s' % os.getpid())#异步相当于线程的存在,用的都是同一个进程id32 '''33 0 a 9606034 0 b 9606035 1 b 9606036 2 b 9606037 1 a 9606038 3 b 9606039 4 b 9606040 2 a 9606041 5 b 9606042 6 b 9606043 3 a 9606044 4 a 9606045 7 b 9606046 8 b 9606047 5 a 9606048 6 a 9606049 7 a 9606050 9 b 9606051 8 a 9606052 9 a 9606053 ['a function', 'b function']54 8.42406249046325755 parent is 9606056 57 Process finished with exit code 058 59 '''9.4.gevent异步模块的使用
1 # coding:utf-8 2 import os 3 import random 4 import time 5 import gevent 6 7 def gevent_a(): 8 for i in range(5): 9 print(i,'a',os.getpid())10 gevent.sleep(random.random()*2)11 return 'a function'12 13 def gevent_b():14 for i in range(5):15 print(i,'b',os.getpid())16 gevent.sleep(random.random()*2)17 return 'b function'18 19 if __name__=='__main__':20 start=time.time()21 g_a=gevent.spawn(gevent_a)22 g_a.run()23 print(time.time()-start)24 print('parent is %s' % os.getpid())#异步相当于线程的存在,用的都是同一个进程id25 '''26 0 a 9978427 1 a 9978428 2 a 9978429 3 a 9978430 4 a 9978431 6.33563661575317432 parent is 9978433 '''1 # coding:utf-8 2 import os 3 import random 4 import time 5 import gevent 6 7 def gevent_a(): 8 for i in range(5): 9 print(i,'a',os.getpid())10 gevent.sleep(random.random()*2)11 return 'a function'12 13 def gevent_b():14 for i in range(5):15 print(i,'b',os.getpid())16 gevent.sleep(random.random()*2)17 return 'b function'18 19 if __name__=='__main__':20 start=time.time()21 g_a=gevent.spawn(gevent_a)22 g_b=gevent.spawn(gevent_b)23 gevent_list=[g_a,g_b]24 result=gevent.joinall(gevent_list)25 print(result)26 print(result[0].value)27 print(time.time()-start)28 print('parent is %s' % os.getpid())#异步相当于线程的存在,用的都是同一个进程id29 '''30 0 a 9984031 0 b 9984032 1 b 9984033 2 b 9984034 1 a 9984035 3 b 9984036 2 a 9984037 4 b 9984038 3 a 9984039 4 a 9984040 [<Greenlet at 0x20ec1afcee8: _run>, <Greenlet at 0x20ec1afcca8: _run>]41 b function42 8.29691696166992243 parent is 9984044 '''9.5.异步总结
异步相对同步而言,异步意味着无序,而同步意味着有序。正因为异步的无序,使得各个程序间的协调成为一大难题。由此,异步编程应运而生。它是以进程,线程,协程,函数/方法作为执行程序的基本单位,结合回调,事件循环,信号量等机制,以提高程序整体执行效率和并发能力的一种编程方式。
在python中,如何实现异步?
方法一:
- 如何定义一个异步: async
- 如何在一个异步程序中,调用另外一个异步,使用关键字 await
- 非异步程序,如何调用异步函数: asyncio
方法二:通过异步包gevent
- 如何创建协程即异步对象:spwan 函数
- 如何批量处理协程对象:joinall
10.多线程多进程面试常问知识点
谈谈你对进程,线程,协程的理解进程:
- 一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,开销大。进程的状态有:新建态,就绪态,运行态,阻塞态,终止态
- 线程: cpu调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在,一个进程至少有一个线 程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
- 协程: 是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和 栈,直接操中栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
什么是多线程竞争
- 线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态,即:数据几乎同步会被多个线程占用,造成数据混乱,即所谓的线程不安全 那么怎么解决多线程竞争问题?—锁
- 锁的好处: 确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资 源竞争下的原子操作问题。
- 锁的坏处: 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下 降了
- 锁的致命问题: 死锁
解释一下什么是锁,什么是死锁
- 锁:当一个线程去访问某一个资源的时候,对这个资源上锁,其他线程就无法对这个资源进行访问,等这个线程处理完成了,对锁进行释放,其他线程再访问这个资源
- 死锁:当因为某些原因,比如锁无法释放,循环等待资源释放的时候,需要该资源的线程一直无法执行,就进入死锁状态
什么是线程安全,什么是互斥锁
- 互斥锁是一种独占锁,同一时刻只有一个线程可以访问共享的数据。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
- 同一个进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一个线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。
python中进程和线程的使用场景
- 多进程适合在CPU密集操作,业务处理(cpu操作指令比较多,如位多的的浮点运算)。
- 多线程适合在IO密性型操作(读写数据操作比多的的,比如爬虫)
11.拓展
Python进程与线程的概念及区别 :
- 进程可以被称为执行的程序,一个进程拥有完整的数据空间和代码空间,每一个进程的地址空 间都是独立的,进程之间不能共享数据。
- 线程是进程的一个实体, 是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本 单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源,但是它可与同属一 个进程的其他的线程共享进程所拥有的全部资源。
- 通俗的讲一条流水线的执行过程是一个线程,一条流水线必须属于一个车间,一个车间的运行 过程就是一个进程。
二者之间的关系:
一个程序至少有一个进程,一个进程至少有一个线程
区别:
- 进程有独立的地址空间,多进程较稳定,因为其中一个出现状况不影响另外一个;同一个进程的多个线程,共用地址空间,多线程相比于多进程,稳定性要差,因为一个线程出现问题会严 重影响其他线程。
- 进程之间需要共享数据,要利用进程间通讯;同一个进程中的线程不需要。
- 进程只是资源分配的最小单位;线程是执行的最小单位,也就是说实际执行的是线程。
CPU密集型和IO密集型:
- CPU密集型代码(各种循环处理、计数等等)
- IO密集型代码(文件处理、网络爬虫等)
线程与进程谁更快:
因为python锁的问题,线程进行锁竞争、切换线程,会消耗资源。在CPU密集型任务下,多进程更快或者说效果更好;而IO密集型,多线程能有效提高效率。