python进阶07并发之三其他问题

何时使用多进程(线程)

使用多进程or线程,对于python,考虑到GIL锁,基本上默认使用多进程就对了。
除此之外,线程共享全局变量,进程全局变量则是隔离的,实际进程大多数情况需要通信的,所以也需要考虑共享数据读写问题处理。 决定因素稳定性和数据共享要求上(操作系统差异性,win偏好线程,linux偏好进程)
性能上虽然线程较好,但实际除了大型互联网公司和部分专业性质软件,大多数中小型公司的并发量,并不会带来很大影响,况且目前服务器领域,基本上LInux和Unix占比较高,线程相比进程在性能上优势并不十分突出。所以这方面考量不会太大的。

以下几种情况可考虑多进程(线程)

1
2
3
循环(互相独立,无内部依赖)
耗时操作,批量下载IO等操作,典型的是网络爬虫
分叉型计算,典型的是分治算法,或mapreduce的map阶段

python多线程既然有GIL锁为何还需要加锁

python进阶08并发之四map, apply, map_async, apply_async差异

差异矩阵

python封装了4种常用方法,用于实现并发
其差异如下

Multi-argsConcurrenceBlockingOrdered-results
mapnoyesyesyes
applyyesnoyesno
map_asyncnoyesnoyes
apply_asyncyesyesnono

需要注意:map 和 map_async 入参为迭代器类型,可以批量调用。而apply和apply_async只能一个个调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# map
results = pool.map(worker, [1, 2, 3])

# apply
for x, y in [[1, 1], [2, 2]]:
results.append(pool.apply(worker, (x, y)))

def collect_result(result):
results.append(result)

# map_async
pool.map_async(worker, jobs, callback=collect_result)

# apply_async
for x, y in [[1, 1], [2, 2]]:
pool.apply_async(worker, (x, y), callback=collect_result)

python进阶09并发之五生产者消费者

这也是实际项目中使用较多的一种并发模式,用Queue(JoinableQueue)实现,是Python中最常用的方式(这里的queue特指multiprocess包下的queue,非queue.Queue)。

Queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/7 14:06'
from multiprocessing import Process,Queue
import time,random

def consumer(q,name):
while True:
food = q.get()
if food is None:
print('接收到了一个空,生产者已经完事了')
break

print('\033[31m{}消费了{}\033[0m'.format(name,food))
time.sleep(random.random())

def producer(name,food,q):
for i in range(10):
time.sleep(random.random())
f = '{}生产了{}{}'.format(name,food,i)
print(f)
q.put(f)



if __name__ == '__main__':
q = Queue(20)
p1 = Process(target=producer,args=('fioman','包子',q))
p2 = Process(target=producer,args=('jingjing','馒头',q))
p1.start()
p2.start()

c1 = Process(target=consumer,args=(q,'mengmeng'))
c2 = Process(target=consumer,args=(q,'xiaoxiao'))
c1.start()
c2.start()

# 让主程序可以等待子进程的结束.
p1.join()
p2.join()
# 生产者的进程结束,这里需要放置两个空值,供消费者获取,用来判断已经没有存货了
q.put(None)
q.put(None)

print('主程序结束..........')

JoinableQueue

创建可连接的共享进程队列,它们也是队列,但是这些队列比较特殊.它们可以允许消费者通知生产者项目已经被成功处理.注意,这里必须是生产者生产完了,生产者的进程被挂起,等到消费者完全消费的时候,生产者进程就结束,然后主程序结束.将消费者进程设置为守护进程,这样的话,主进程结束的时候,消费进程也就结束了.
JoinableQueue()比普通的Queue()多了两个方法:

python进阶05并发之一基本概念

大多编程语言,一旦涉及并发,都会比较复杂,知识点也较多(大多为历史问题,很多技术点现在非常少使用了,但语言层面也提供支持,对于这些冷门点,只需要知道即可,使用时也尽量避免使用这种冷门技术,除非和应用场景非常匹配)。实际使用过程中,只需要知道各名词以及大概功能,大多现用现查,毕竟涉及点太多,而且使用频率也并非很高,一般也就新系统研发会使用,后续维护时基本不会涉及太多。

进程状态和调度

进程,线程,协程

python进阶06并发之二技术点关键词

GIL,线程锁

python中存在GIL这个”线程锁”,
关键地方可以使用c语言解决 GIL问题 然后可以提高cpu占用效率

守护进程

主进程创建守护进程
1)守护进程会在主进程代码执行结束后就终止
2)守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#主进程代码运行完毕,守护进程就会结束
from multiprocessing import Process
from threading import Thread
import time
def foo():
print(123)
time.sleep(1)
print("end123")

def bar():
print(456)
time.sleep(3)
print("end456")


p1=Process(target=foo)
p2=Process(target=bar)

p1.daemon=True
p1.start()
p2.start()
print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×