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

python

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)- 进程与线程编程 的全部内容, 来源链接: utcz.com/z/387858.html

回到顶部