Python学习day40-并发编程(终)

 

Python学习day40-并发编程(终)

线程queue和定时器

线程队列里面有几种有意思的算法模型,好比队列,堆栈,以及有优先级的取数据,是比较好玩的几个东西.实例见下面:html

 
 
 
xxxxxxxxxx
22
 
 
 
 
1
import queue
2
# 队列:先进先出
3
q = queue.Queue()
4
q.put('123')
5
q.put('456')
6
print(q.get())
7
print(q.get())
8
'''
9
123
10
456
11
Process finished with exit code 0
12
能够看到,程序到此已经结束,其取数据的顺序就是先进先出
13
14
比较有趣的是,队列里面还有task_done和join两个方法,其用法和以前所了解的JoinableQueue里面的task_done和join是彻底同样的,即有几个put就要有几个task_done,否则join就会卡主,不会释放程序
15
'''
16
q.task_done()
17
# q.task_done()
18
q.join()# 这里就会卡主,程序不会继续向下执行,也不会自动结束
19
'''
20
123
21
456
22
'''
 
 
 
 
 
xxxxxxxxxx
15
 
 
 
 
1
import queue
2
# 堆栈,先进后出
3
q = queue.LifoQueue()
4
q.put('123')
5
q.put('456')
6
q.put('789')
7
print(q.get())
8
print(q.get())
9
print(q.get())
10
'''
11
结果:
12
789
13
456
14
123
15
'''
 
 
 
 
 
xxxxxxxxxx
18
 
 
 
 
1
import queue
2
# 能够根据优先级取数据
3
4
q = queue.PriorityQueue()
5
# 这里要注意,put里面的第一个参数一般为int类型,并且值越小优先级越高,包括负数,意思就是全部的负数优先级都高于整数,
6
q.put((-1, 'a'))
7
q.put((80, 'b'))
8
q.put((1, 'c'))
9
10
print(q.get())
11
print(q.get())
12
'''
13
结果为:
14
(-1, '1')
15
(1, '3')
16
(80, '2')
17
Process finished with exit code 0
18
'''
 
 

定时器,顾名思义,就是定时的意思,咱们设定一个延时,就可让进程延时这些时间而后再开启.实例以下:node

 
 
 
xxxxxxxxxx
60
 
 
 
 
1
from threading import Thread, current_thread, Timer
2
import time
3
4
5
def task():
6
    end = time.time()
7
    print(f'线程执行了')
8
    time.sleep(2)
9
    print(f'线程结束了')
10
    print(f'进程开启延时了{end - start:6.2f}秒')
11
12
13
if __name__ == '__main__':
14
    start = time.time()
15
    t = Timer(4, task)  # Timer()括号里第一个参数为延时的时间,第二个为开启的线程要运行的函数,这个例子是过4秒后开启一个线程
16
    t.start()
17
'''
18
执行结果为:
19
线程执行了
20
线程结束了
21
进程开启延时了 4.00秒
22
Process finished with exit code 0
23
24
25
到这里咱们可能会有个疑问,那么这种延时和直接用time.sleep()有什么区别呢?
26
区别就在于time.sleep()是整个程序都暂停了,而Timer只是延时开启进程,对于别的代码的运行没有任何影响,咱们能够在main程序里面加入两个print来证实这一点
27
28
'''
29
from threading import Thread, current_thread, Timer
30
import time
31
32
33
def task():
34
    end = time.time()
35
    print(f'线程执行了')
36
    time.sleep(2)
37
    print(f'线程结束了')
38
    print(f'进程开启延时了{end - start:6.2f}秒')
39
40
41
if __name__ == '__main__':
42
    print('进入main')
43
    start = time.time()
44
    print('准备进入延时')
45
    t = Timer(4, task)  
46
    t.start()
47
    print('main结束')
48
'''
49
执行结果为:
50
进入main
51
准备进入延时
52
main结束
53
线程执行了
54
线程结束了
55
进程开启延时了 4.00秒
56
57
Process finished with exit code 0
58
59
上面的执行结果咱们就能够看出,延时并无影响其他代码的运行,这点很是难得
60
'''
 
 

 

线程池和进程池

首先咱们要明白进程池和线程池的概念是什么,所谓池,池子,现实生活中是盛水的,能够说是一个容器,在咱们的Python里面也是一个容器,用来容纳进程或者线程.python

那么进程池/线程池的做用就是,当计算机须要并发的任务量远远大于计算机所能承受的范围的时候,或者说计算机没法一次性开启过多的任务数的时候,咱们就须要进程池/线程池来限制当前计算机的进程数/线程数,从而保护计算机,或者是服务器,使其不至于宕机,影响业务.web

池所用的模块是ProcessPoolExecutor和 ThreadPoolExecutor,经常使用的一些方法包括:算法

result(),等待该进程/线程的返回结果,当结果都返回以后,result()才会打印出来而后执行后面的代码,这样会致使全部的任务变成串行,一个线程执行完才会去执行下一个线程,虽然下降了效率,可是提升了程序的安全性,条理性也会更清楚编程

submit(obj,*args,**kwargs)第一个参数就是所调用的函数名,第二个和第三个都是调用的函数所须要传入的参数,submit会有一个返回值,因此一般所调用的函数会有返回值,来做为submit的返回值.windows

shutdown()关闭当前进程池/线程池安全

例程以下:服务器

 
 
 
xxxxxxxxxx
62
 
 
 
 
1
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
2
from threading import currentThread
3
from multiprocessing import current_process
4
import time
5
6
7
def task(i):
8
    print(f'开启{i}')
9
    print(f'线程{currentThread().name}执行任务{i}')
10
    # print(f'进程{current_process().name}执行任务{i}')
11
    time.sleep(2)
12
    print(f'线程{currentThread().name}结束任务{i}')
13
    print(f"\033[5;31;40m结束{i}\033[0m")
14
15
    return i ** 2
16
17
    # print(f'进程{current_process().name}结束任务{i}')
18
19
20
if __name__ == '__main__':
21
    # pool_p = ProcessPoolExecutor(4)
22
    pool_t = ThreadPoolExecutor(4)
23
    fu_list = []
24
    for i in range(5):
25
        # submit括号里第一个参数是所调用的函数名task,第二个和第三个是须要传入的调用函数task的形参,
26
        # submit能够返回一个对象,因此能够在task函数里面写返回值
27
        future = pool_t.submit(task, i)  # 5个线程,解决20个任务
28
        print(f'执行结果为{future.result()}')
29
        # 若是没有结果,会一直等待拿到结果,就会致使全部的任务变成串行,一个线程运行完才会运行下一个任务
30
        # pool_p.submit(task, i)
31
    pool_t.shutdown()  # 会关闭池的入口,会等到全部的任务执行完,结束阻塞
32
    for fu in fu_list:
33
        print(fu.result())
34
35
'''
36
开启0
37
线程ThreadPoolExecutor-0_0执行任务0
38
线程ThreadPoolExecutor-0_0结束任务0
39
结束0
40
执行结果为0
41
开启1
42
线程ThreadPoolExecutor-0_0执行任务1
43
线程ThreadPoolExecutor-0_0结束任务1
44
结束1
45
执行结果为1
46
开启2
47
线程ThreadPoolExecutor-0_1执行任务2
48
线程ThreadPoolExecutor-0_1结束任务2
49
结束2
50
执行结果为4
51
开启3
52
线程ThreadPoolExecutor-0_0执行任务3
53
线程ThreadPoolExecutor-0_0结束任务3
54
结束3
55
执行结果为9
56
开启4
57
线程ThreadPoolExecutor-0_2执行任务4
58
线程ThreadPoolExecutor-0_2结束任务4
59
结束4
60
执行结果为16
61
Process finished with exit code 0
62
'''
 
 

同步和异步

同步和异步实际上是提交任务的两种方式,简单来讲,并发

  1. 同步就是提交了一个任务,必须等任务执行完了才能执行下一行代码
  2. 异步则是提交了一个任务,不须要等任务执行完,就能够执行下面的代码.

协程

简单来讲:协程是一种用户态的轻量级的线程,是由用户程序本身控制调度的

协程须要注意的两点是:

  1. Python的线程是内核级别的,是由操做系统控制调度的
  2. 单线程内开启的协程,一旦遇到IO就会从应用程序级别而不是操做系统级别进行切换,从而提高效率,这就是协程存在的意义.

为了正确的使用协程,咱们须要引入gevent模块(这个不是Python自带的模块,须要读者本身下载,指令pip3 install gevent),并且须要使用到gevent的一个很是有趣的魔法方法,叫作moncky,具体见下例

 
 
 
xxxxxxxxxx
1
28
 
 
 
 
1
from gevent import monkey;monkey.patch_all()  # 咱们须要在开头加上这一句,由于gevent自己并不能捕获全部的IO,只能识别自身的延时,因此这个语句至关于一个补丁,能够识别程序里面全部的IO,而后遇到这些IO的时候切换至其余的协程
2
3
import gevent# 导入gevent模块,这个导入必定要在上面的语句下面,切记!
4
import time
5
6
7
def eat():
8
    print('eat 1')
9
    time.sleep(2)
10
    print('eat 2')
11
12
13
def play():
14
    print('play 1')
15
    time.sleep(3)
16
    print('play 2')
17
18
19
start = time.time()
20
g1 = gevent.spawn(eat)# gevent的用法不一样于以前学习的进程和线程,是另外一种调用方法,并且下面必定须要用join来阻塞才能得到每一个协程的执行结果,不然程序不会返回任何值,也不会执行开启协程所写入的那个任务的内容
21
g2 = gevent.spawn(play)
22
g1.join()
23
g2.join()
24
end = time.time()
25
print(f'{end - start:6.6f}')
26
相关文章
相关标签/搜索