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()-start
- 20 print(end)
- 21 print('parent pid is %s' % os.getpid())
- 22 '''
- 23 0 a 36436
- 24 1 a 36436
- 25 2 a 36436
- 26 0 b 36436
- 27 1 b 36436
- 28 2 b 36436
- 29 6.002437114715576
- 30 parent pid is 36436 所有的进程id跟主进程一致,都是36436
- 31 '''
- 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()获取进程id
- 12 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()-start
- 25 print(end)
- 26 print('parent pid is %s' % os.getpid())
- 27 '''
- 28 0 b 35196
- 29 0 a 24640
- 30 1 b 35196
- 31 1 a 24640
- 32 2 b 35196
- 33 2 a 24640
- 34 3.0127575397491455
- 35 parent pid is 35196
- 36 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()获取进程id
- 12 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) #子进程1
- 22 b_p=multiprocessing.Process(target=work_b) #子进程2
- 23 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.0
- 30 parent pid is 19692
- 31 0 a 35408
- 32 0 b 34000
- 33 1 a 35408
- 34 1 b 34000
- 35 2 a 35408
- 36 2 b 34000
- 37 '''
- 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()获取进程id
- 12 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) #子进程1
- 22 b_p=multiprocessing.Process(target=work_b) #子进程2
- 23 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 35816
- 31 1 a 35816
- 32 2 a 35816
- 33 0 b 35172
- 34 1 b 35172
- 35 2 b 35172
- 36 6.118025302886963
- 37 parent pid is 14624
- 38 '''
- 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()获取进程id
- 12 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) #子进程1
- 22 b_p=multiprocessing.Process(target=work_b) #子进程2
- 23 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.016000986099243164
- 32 parent pid is 35268
- 33 0 a 34584
- 34 1 a 34584
- 35 2 a 34584
- 36 '''
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 10092
- 26 1 19704
- 27 2 36164
- 28 3 37212
- 29 4 34628
- 30 5 10092
- 31 6 19704
- 32 7 36164
- 33 8 37212
- 34 9 34628
- 35 10 10092
- 36 11 19704
- 37 12 36164
- 38 13 37212
- 39 14 34628
- 40 15 10092
- 41 16 19704
- 42 17 36164
- 43 18 37212
- 44 19 34628
- 45 '''
- 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 10092
- 26 1 19704
- 27 2 36164
- 28 3 37212
- 29 4 34628
- 30 5 10092
- 31 6 19704
- 32 7 36164
- 33 8 37212
- 34 9 34628
- 35 10 10092
- 36 11 19704
- 37 12 36164
- 38 13 37212
- 39 14 34628
- 40 15 10092
- 41 16 19704
- 42 17 36164
- 43 18 37212
- 44 19 34628
- 45 '''
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 = result
- 21 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=q
- 10
- 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 = result
- 28 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 return
- 13 data=random.choice(lists)
- 14 lists.remove(data)
- 15 new_data='%s_new' % data
- 16 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.003076553344727
- 30 '''
- 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 return
- 13 data=random.choice(lists)
- 14 lists.remove(data)
- 15 new_data='%s_new' % data
- 16 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.0019862651824951
- 36 '''
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 0
- 16 1
- 17 23
- 18
- 19 4
- 20 5
- 21 6
- 22 7
- 23 8
- 24 9
- 25 '''
- 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 0
- 22 1
- 23 2
- 24 3
- 25 4
- 26 5
- 27 6
- 28 7
- 29 8
- 30 9
- 31 '''
- 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' % i
- 12
- 13 if __name__=="__main__":
- 14 print(os.getpid()) #57764
- 15 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 0
- 26 result 1
- 27 result 2
- 28 result 3
- 29 result 4
- 30 result 5
- 31 result 6
- 32 result 7
- 33 result 8
- 34 result 9
- 35 '''
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 a
- 26 1 a
- 27 2 a
- 28 3 a
- 29 4 a
- 30 5 a
- 31 6 a
- 32 7 a
- 33 8 a
- 34 9 a
- 35 0 b
- 36 1 b
- 37 2 b
- 38 3 b
- 39 4 b
- 40 5 b
- 41 6 b
- 42 7 b
- 43 8 b
- 44 9 b
- 45 19.68557095527649
- 46 '''
- 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 a
- 32 0 b
- 33 1 b
- 34 2 b
- 35 1 a
- 36 3 b
- 37 2 a
- 38 3 a
- 39 4 b
- 40 4 a
- 41 5 a
- 42 5 b
- 43 6 a
- 44 7 a
- 45 6 b
- 46 8 a
- 47 7 b
- 48 9 a
- 49 8 b
- 50 9 b
- 51 ['a function', 'b function']
- 52 10.612190246582031
- 53 '''
- 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 b
- 33 1 b
- 34 2 b
- 35 3 b
- 36 4 b
- 37 5 b
- 38 6 b
- 39 7 b
- 40 8 b
- 41 9 b
- 42 6.932542085647583
- 43 '''
- 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())#异步相当于线程的存在,用的都是同一个进程id
- 32 '''
- 33 0 a 96060
- 34 0 b 96060
- 35 1 b 96060
- 36 2 b 96060
- 37 1 a 96060
- 38 3 b 96060
- 39 4 b 96060
- 40 2 a 96060
- 41 5 b 96060
- 42 6 b 96060
- 43 3 a 96060
- 44 4 a 96060
- 45 7 b 96060
- 46 8 b 96060
- 47 5 a 96060
- 48 6 a 96060
- 49 7 a 96060
- 50 9 b 96060
- 51 8 a 96060
- 52 9 a 96060
- 53 ['a function', 'b function']
- 54 8.424062490463257
- 55 parent is 96060
- 56
- 57 Process finished with exit code 0
- 58
- 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())#异步相当于线程的存在,用的都是同一个进程id
- 25 '''
- 26 0 a 99784
- 27 1 a 99784
- 28 2 a 99784
- 29 3 a 99784
- 30 4 a 99784
- 31 6.335636615753174
- 32 parent is 99784
- 33 '''
- 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())#异步相当于线程的存在,用的都是同一个进程id
- 29 '''
- 30 0 a 99840
- 31 0 b 99840
- 32 1 b 99840
- 33 2 b 99840
- 34 1 a 99840
- 35 3 b 99840
- 36 2 a 99840
- 37 4 b 99840
- 38 3 a 99840
- 39 4 a 99840
- 40 [<Greenlet at 0x20ec1afcee8: _run>, <Greenlet at 0x20ec1afcca8: _run>]
- 41 b function
- 42 8.296916961669922
- 43 parent is 99840
- 44 '''
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密集型,多线程能有效提高效率。
Python语法进阶(1)- 进程与线程编程的更多相关文章
- Python全栈【进程、线程】
Python全栈[进程.线程] 本节内容: 进程 线程 协程 I/O多路复用 进程 1.进程就是一个程序在一个数据集上的一次动态执行过程,进程是资源分配的最小单元. 2.进程一般由程序.数据集.进程控 ...
- [ Python - 14 ] python进程及线程编程
什么是进程: 简单来讲,进程就是操作系统中运行的程序或任务,进程和程序的区别在于进程是动态的,而程序是静态的.进程是操作系统资源管理的最小单位. 什么是线程: 线程是进程的一个实体,是cpu调度和分派 ...
- python 学习笔记八 进程和线程 (进阶篇)
什么是线程(thread)? 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执 ...
- Python之路,Day9, 进程、线程、协程篇
本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...
- Python学习之路--进程,线程,协程
进程.与线程区别 cpu运行原理 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Q ...
- Python语法速查: 20. 线程与并发
返回目录 本篇索引 (1)线程基本概念 (2)threading模块 (3)线程间同步原语资源 (4)queue (1)线程基本概念 当应用程序需要并发执行多个任务时,可以使用线程.多个线程(thre ...
- Python socket进阶 多线程/进程
#首先,什么场合下用进程,什么场合下用线程: . 计算密集型的用进程. . IO密集型的用进程. xSocket语法及相关 Socket Families(地址簇) socket.AF_UNIX un ...
- Python学习笔记 - day13 - 进程与线程
概述 我们都知道windows是支持多任务的操作系统. 什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务.打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多 ...
- python(40)- 进程、线程、协程及IO模型
一.操作系统概念 操作系统位于底层硬件与应用软件之间的一层.工作方式:向下管理硬件,向上提供接口. 操作系统进行进程切换:1.出现IO操作:2.固定时间. 固定时间很短,人感受不到.每一个应用层运行起 ...
随机推荐
- session反序列化
先来了解一下关于session的一些基础知识 什么是session?在计算机中,尤其是在网络应用中,称为"会话控制".Session 对象存储特定用户会话所需的属性及配置信息.这样 ...
- 【Java】final
final final可以用来修饰的结构:类.方法.变量 final 用来修饰一个类:此类不能被其他类所继承. 比如:String类.System类.StringBuffer类 final 用来修饰方 ...
- 【PTA】6-1 **删除C程序中的注释 (31 分)
请你编写一个函数,将C语言源程序中的注释全部删去. 函数原型 // 删除注释 void Pack(FILE *src, FILE *dst); 说明:参数 src 和 dst 均为文件指针,其中:sr ...
- JUC并发编程与高性能内存队列disruptor实战-上
JUC并发实战 Synchonized与Lock 区别 Synchronized是Java的关键字,由JVM层面实现的,Lock是一个接口,有实现类,由JDK实现. Synchronized无法获取锁 ...
- ctf--web刷题记录 ACTF2020back up file 、极客大挑战2019php、secret file
ACTF2020back up file backup file指的是备份文件,一般备份文件的后缀有".git" .".svn"." .swp&quo ...
- 1012day-人口普查系统
1.name.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pa ...
- 【刷题-LeetCode】229. Majority Element II
Majority Element II Given an integer array of size n, find all elements that appear more than ⌊ n/3 ...
- Cesium源码剖析---视频投影
Cesium中的视频投影是指将视频作为一种物体材质,实现在物体上播放视频的效果.这个功能在Cesium早期版本中就支持了,在Code Example中有一个示例.今天就来分析一下其内部实现原理. 1. ...
- 快速删除IDEA/WebStrom/Rider中的代码空行
使用替换 ^\s*\n 并打开正则匹配模式 Visual Studio中未测试,大家可以去试一试
- 在EntityFrameworkCore中记录EF修改日志,保存,修改字段的原始值,当前值,表名等信息
突发奇想,想把业务修改的所有字段原始值和修改后的值,做一个记录,然后发现使用EF可以非常简单的实现这个功能 覆盖父类中的 SaveShanges() 方法 public new int SaveCha ...