Python语法进阶(1)-进程与线程编程

博客 分享
0 280
张三
张三 2022-02-16 21:55:26
悬赏:0 积分 收藏

Python语法进阶(1)- 进程与线程编程

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密集型,多线程能有效提高效率。

posted @ 2022-02-16 19:08 葛老头 阅读(10) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员