搜索
您的当前位置:首页正文

Python学习day36-并发编程(2)

来源:小奈知识网

 

Python学习day36-并发编程(2)

Process的一些常用方法和属性

join

join的主要用法是在于可以让主进程等待子进程运行完后,再执行下面的语句.要注意的一点是,主进程在等待,但是子进程在正常运行.

下面是join一些简单用法,包括串行,并行以及并行的优化

 
 
 
xxxxxxxxxx
118
 
 
 
 
1
from multiprocessing import Process
2
import time
3
4
def foo():
5
print('进程start')
6
time.sleep(2)
7
print('进程end')
8
if __name__ == '__main__':
9
p = Process(target=foo)
10
p.start()
11
time.sleep(5)
12
p.join()  # 要等待该子进程全部执行完才会继续向下运行,主进程在等待,子进程在运行,内部会调用wait(),回收其子进程的pid号
13
print('主')
14
    
15
    
16
# 输出结果为:
17
# 进程start
18
# 进程end
19
# 主
20
21
22
23
# 串行,开辟内存空间要多费时间
24
def foo(x):
25
print(f'{x}进程start')
26
time.sleep(x)
27
print(f'{x}进程end')
28
if __name__ == '__main__':
29
p = Process(target=foo, args=(3,))
30
p2 = Process(target=foo, args=(2,))
31
p3 = Process(target=foo, args=(1,))
32
start = time.time()
33
p.start()
34
p.join()
35
p2.start()
36
p2.join()
37
p3.start()
38
p3.join()
39
# time.sleep(5)
40
end = time.time()
41
print(f'{end-start}')
42
print('主')
43
# 结果为:
44
# 3进程start
45
# 3进程end
46
# 2进程start
47
# 2进程end
48
# 1进程start
49
# 1进程end
50
# 6.7584381103515625这个时间不是绝对的,会根据所在机器的性能变化
51
# 主
52
53
54
55
# 并行,真正的同时运行
56
def foo(x):
57
print(f'{x}进程start')
58
time.sleep(x)
59
print(f'{x}进程end')
60
if __name__ == '__main__':
61
p = Process(target=foo, args=(10,))
62
p2 = Process(target=foo, args=(2,))
63
p3 = Process(target=foo, args=(1,))
64
start = time.time()
65
p.start()
66
p2.start()
67
p3.start()
68
# time.sleep(5)
69
p.join()  
70
p2.join()
71
p3.join()
72
end = time.time()
73
print(f'{end-start}')
74
print('主')
75
76
# 结果为:
77
# 2进程start
78
# 3进程start
79
# 1进程start
80
# 1进程end
81
# 2进程end
82
# 3进程end
83
# 3.3003978729248047
84
# 主
85
86
87
# 上述这种并行的发生看起来非常的麻烦,所以我们其实可以用一个循环把下面重复性的创建进程以及join暂停进程都放到循环里,简化代码.
88
# 并行的优化
89
90
def foo(x):
91
print(f'{x}进程start')
92
time.sleep(x)
93
print(f'{x}进程end')
94
95
96
if __name__ == '__main__':
97
start = time.time()
98
p_list = []# 创建一个p_list列表用来接收创建的子进程
99
for i in range(1, 4):
100
p = Process(target=foo, args=(i,))
101
p.start()
102
p_list.append(p)
103
for p in p_list:#将列表里的子进程一一拿出来循环,然后通过join分别暂停主进程,从而达到并行的目的
104
p.join()
105
end = time.time()
106
print(end - start)
107
print('主')
108
    
109
    
110
# 结果为:
111
# 2进程start
112
# 1进程start
113
# 3进程start
114
# 1进程end
115
# 2进程end
116
# 3进程end
117
# 3.275794744491577
118
# 主
 
 

 

获取当前进程pid和父进程pid

获取当前进程pid的常用方式有两种,分别是:

os.getpid(),这是os模块所带有的方法,可以查看当前进程的pid号

os.getppid(),注意这里是ppid,多了一个p,是查看当前进程的父进程的pid号

子进程对象.pid

 
 
 
xxxxxxxxxx
30
 
 
 
 
1
from multiprocessing import Process, current_process  # 获取当前进程的对象
2
3
import time, os
4
5
6
def task():
7
print('子进程start')
8
print('在子进程中查看自身的pid', current_process().pid)
9
print('在子进程中查看自身的pid', os.getpid())
10
print('在子进程中查看父进程的pid', os.getppid())
11
time.sleep(200)
12
print('子进程end')
13
14
15
if __name__ == '__main__':
16
p = Process(target=task)
17
p.start()
18
print('在主进程查看子进程的pid', p.pid)  # 一定写在子进程启动之后,即start之后
19
print('主进程pid', os.getpid())
20
print('主')
21
    
22
    
23
# 运行结果为:(这里pid每次执行都是不一样的,都是重新分配的)
24
# 在主进程查看子进程的pid 9964
25
# 主进程pid 11584
26
# 主
27
# 子进程start
28
# 在子进程中查看自身的pid 9964
29
# 在子进程中查看自身的pid 9964
30
# 在子进程中查看父进程的pid 11584
 
 

name和is_alive

name() 进程名称,可以手动定义

is_alive() 判断进程是否还存在,只要运行完了就返回false,即便是僵尸进程也是false

 
 
 
xxxxxxxxxx
31
 
 
 
 
1
from multiprocessing import Process, current_process
2
import time
3
4
5
def foo():
6
print('进程start')
7
print(current_process().name)
8
time.sleep(2)
9
print('进程end')
10
11
12
if __name__ == '__main__':
13
p = Process(target=foo, name='nick')
14
# p2 = Process(target=foo, name='jack')
15
# p3 = Process(target=foo, name='mata')
16
p.start()
17
# p2.start()
18
# p3.start()
19
print(p.is_alive())# 判定进程是否还活着,返回值是True活着False
20
print(p.name)# 输出进程的名字,也可以是自身赋予的值,如果自己没有赋值系统默认的就是Process-数字
21
# print(p2.name)
22
# print(p3.name)
23
24
    
25
# 结果是:
26
# True
27
# nick
28
# 进程start
29
# nick
30
# 进程end
31
 
 

 

terminate

手动结束子进程,但其实只是给操作系统发送了一个关闭进程的请求,需要时间才能完成,推荐在后面加上p.join()来实时判断进程是否已经结束了,也就是说进程只有执行完了才会进行下面的语句.

 
 
 
xxxxxxxxxx
18
 
 
 
 
1
from multiprocessing import Process, current_process
2
import time
3
4
5
def foo():
6
print('进程start')
7
print(current_process().name)
8
time.sleep(2)
9
print('进程end')
10
11
12
if __name__ == '__main__':
13
p = Process(target=foo, name='nick')
14
p.start()
15
p.terminate()
16
# p.join()
17
print(p.is_alive())
18
# print(p.name)
 
 

 

daemon

守护进程(本质就是一个子进程)

 
 
 
x
 
 
 
 
1
from multiprocessing import Process, current_process
2
import time
3
4
5
def foo():
6
print('守护进程start', time.time())
7
time.sleep(3)
8
print('守护进程end', time.time())
9
10
11
def task():
12
print('子进程start', time.time())
13
time.sleep(5)
14
print('子进程end', time.time())
15
16
17
if __name__ == '__main__':
18
p = Process(target=foo, name='nick')
19
p2 = Process(target=task, name='nick')
20
p.daemon = True  # 定义为守护进程,必须要在start之前定义
21
p.start()
22
p2.start()
23
time.sleep(2)
24
print('主', time.time())
25
# 结果为:
26
# 守护进程start 1568278460.6168563
27
# 子进程start 1568278460.6188455
28
# 主 1568278462.376258
29
# 子进程end 1568278465.6189692
30
31
32
# 可以看到上述例程的结果里没有守护进程结束的输出语句,因为父进程已经结束了,所以守护进程跟随父进程结束了,后面的就没有打印.
 
 

 

Tips:抢票软件的原理

先来语言分析一波,抢票的总体流程是怎样的,12306的数据库里肯定有实时的票数以及票价,我们购买的时候呢,就要发一个请求,包括查询和购买都要发请求,然后12306的服务器给我们返回数据库里的票数,可是我们都知道,网络是有延迟的,数据传输也是需要时间的,所以很可能同时有很多人,同时查询这个车次的票,然后得到还有一张票的消息(比如),然后这些人都点击购买,那么12306会把这个票给哪个用户呢?

其实这种情况下我们如果用并行进程的概念是行不通的,因为延迟的情况下就有可能很多人都买到了这张票,可是实际情况是不可能的,所以这里为了数据的安全以及逻辑的缜密,我们就要牺牲效率,转而用串行的方法来处理这个事情,尽管效率会慢很多,但是不会出现一张票同时卖给很多人的情况.也就是说用join方法来实现串行.

例程如下:

 
 
 
x
 
 
1
# 这里我们需要在文件的同目录创建一个db.txt文件
2
# 在文件里写{"count": 1}
3
from multiprocessing import Process, current_process
4
import time
5
import json
6
import os
7
8
9
def search():
10
    time.sleep(1)  # 模拟网络io
11
    with open('db.txt', 'rt', encoding='utf-8')as f:
12
        res = json.load(f)
13
        print(f'还剩{res["count"]}张票')
14
15
16
def get():
17
    with open('db.txt', 'rt', encoding='utf-8')as f:
18
        res = json.load(f)
19
        print(f'还剩{res["count"]}张票')
20
    time.sleep(1)  # 模拟网络io
21
    if res['count'] > 0:
22
        res['count'] -= 1
23
        with open('db.txt', 'wt', encoding='utf-8')as f:
24
            json.dump(res, f)
25
            f.flush()
26
            print(f'{os.getpid()}抢票成功')
27
            time.sleep(1)  # 模拟网络io
28
29
    else:
30
        print('票已经售空!~!~!~!~!~!~!~!')
31
    # current_process().terminate()
32
33
 
 
34
def task():
35
    #search()
36
    get()
37
38
39
if __name__ == '__main__':
40
    for i in range(5):
41
        p = Process(target=task)
42
        p.start()
43
        p.join()
44
# 输出结果为:
45
# 还剩1张票
46
# 11576抢票成功
47
# 还剩0张票
48
# 票已经售空!~!~!~!~!~!~!~!
49
# 还剩0张票
50
# 票已经售空!~!~!~!~!~!~!~!
51
# 还剩0张票
52
# 票已经售空!~!~!~!~!~!~!~!
53
# 还剩0张票
54
# 票已经售空!~!~!~!~!~!~!~!
55
 
 

当然实际的抢票软件肯定做了更多的优化,不会这么简单,但是大概逻辑是差不多的.

 

转载于:https://www.cnblogs.com/Xu-PR/p/11512910.html

因篇幅问题不能全部显示,请点此查看更多更全内容

Top