进程
在python中multiprocess模块提供了Process类,实现进程相关的功能。但是,由于它是基于fork机制的,因此不被windows平台支持。想要在windows中运行,必须使用if __name__ == '__main__':的方式,显然这只能用于调试和学习,不能用于实际环境。
下面是一个简单的多进程例子
from multiprocessing import Process
import time
def f(name):
time.sleep(2)
print('hello', name)
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()
显示单独的进程ID
from multiprocessing import Process
import os
def info(title):
print(title)
print('module name:', __name__)
print('parent process:', os.getppid())
print('process id:', os.getpid())
print("\n\n")
def f(name):
info('\033[31;1mfunction f\033[0m')
print('hello', name)
if __name__ == '__main__':
info('\033[32;1mmain process line\033[0m')
p = Process(target=f, args=('bob',))
p.start()
p.join()
进程间通讯
不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法
Queues
使用方法跟threading里的queue差不多
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
Pipes
利用管道来实现数据交换
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello'])
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, 'hello']"
p.join()
Managers
使用Managers来共享数据
from multiprocessing import Process, Manager
import os
def f(d, l):
d[os.getpid()] = os.getpid()
l.append(os.getpid())
print(l)
if __name__ == '__main__':
with Manager() as manager:
d = manager.dict()
l = manager.list(range(5))
p_list = []
for i in range(10):
p = Process(target=f, args=(d, l))
p.start()
p_list.append(p)
for res in p_list:
res.join()
print(d)
print(l)
进程同步
为了防止和多线程一样的出现数据抢夺和脏数据的问题,同样需要设置进程锁。与threading类似,在multiprocessing里也有同名的锁类RLock, Lock, Event, Condition, Semaphore,连用法都是一样的!
from multiprocessing import Process, Lock
def f(l, i):
l.acquire()
try:
print('hello world', i)
finally:
l.release()
if __name__ == '__main__':
lock = Lock()
for num in range(10):
Process(target=f, args=(lock, num)).start()
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
- apply
- apply_async
from multiprocessing import Process, Pool,freeze_support
import time
import os
def Foo(i):
time.sleep(2)
print("in process",os.getpid())
return i + 100
def Bar(arg):
print('-->exec done:', arg,os.getpid())
if __name__ == '__main__':
#freeze_support()
pool = Pool(processes=5) #允许进程池同时放入5个进程
print("主进程",os.getpid())
for i in range(10):
pool.apply_async(func=Foo, args=(i,), callback=Bar) #callback=回调
#pool.apply(func=Foo, args=(i,)) #串行
#pool.apply_async(func=Foo, args=(i,)) #串行
print('end')
pool.close()
pool.join() #进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。.join()
协程
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:
协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。
协程的好处:
- 无需线程上下文切换的开销
- 无需原子操作锁定及同步的开销
- 方便切换控制流,简化编程模型
- 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
- 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
- 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
使用yield实现协程操作例子
import time
import queue
def consumer(name):
print("--->starting eating baozi...")
while True:
new_baozi = yield
print("[%s] is eating baozi %s" % (name,new_baozi))
#time.sleep(1)
def producer():
r = con.__next__()
r = con2.__next__()
n = 0
while n < 5:
n +=1
con.send(n)
con2.send(n)
print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
if __name__ == '__main__':
con = consumer("c1")
con2 = consumer("c2")
p = producer()
Greenlet
from greenlet import greenlet
def test1():
print 12
gr2.switch()
print 34
gr2.switch()
def test2():
print 56
gr1.switch()
print 78
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
Gevent
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。
import gevent
def foo():
print('Running in foo')
gevent.sleep(0)
print('Explicit context switch to foo again')
def bar():
print('Explicit context to bar')
gevent.sleep(0)
print('Implicit context switch back to bar')
gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])
输出:
Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar
遇到IO阻塞时会自动切换任务
from urllib import request
import gevent,time
from gevent import monkey
monkey.patch_all() #把当前程序的所有的io操作给我单独的做上标记
def f(url):
print('GET: %s' % url)
resp = request.urlopen(url)
data = resp.read()
print('%d bytes received from %s.' % (len(data), url))
urls = ['https://www.python.org/',
'https://www.yahoo.com/',
'https://github.com/' ]
time_start = time.time()
for url in urls:
f(url)
print("同步cost",time.time() - time_start)
async_time_start = time.time()
gevent.joinall([
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://github.com/'),
])
print("异步cost",time.time() - async_time_start)
通过gevent实现单线程下的多socket并发
import sys
import socket
import time
import gevent
from gevent import socket, monkey
monkey.patch_all()
def server(port):
s = socket.socket()
s.bind(('0.0.0.0', port))
s.listen(500)
while True:
cli, addr = s.accept()
gevent.spawn(handle_request, cli)
def handle_request(conn):
try:
while True:
data = conn.recv(1024)
print("recv:", data)
conn.send(data)
if not data:
conn.shutdown(socket.SHUT_WR)
except Exception as ex:
print(ex)
finally:
conn.close()
if __name__ == '__main__':
server(8001)
论事件驱动与异步IO
通常,我们写服务器处理模型的程序时,有以下几种模型:
(1)每收到一个请求,创建一个新的进程,来处理该请求;
(2)每收到一个请求,创建一个新的线程,来处理该请求;
(3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求
上面的几种方式,各有千秋,
第(1)中方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式
Select\Poll\Epoll异步IO
select
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。
另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。
poll
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
epoll
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
select 多并发socket 例子
import select
import socket
import queue
server = socket.socket()
server.bind(('localhost',9000))
server.listen(1000)
server.setblocking(False) #不阻塞
msg_dic = {}
inputs = [server,]
#inputs = [server,conn] #[conn,]
#inputs = [server,conn,conn2] #[conn2,]
outputs = [] #
#outputs = [r1,] #
while True:
readable ,writeable,exceptional= select.select(inputs, outputs, inputs )
print(readable,writeable,exceptional)
for r in readable:
if r is server: #代表来了一个新连接
conn,addr = server.accept()
print("来了个新连接",addr)
inputs.append(conn) #是因为这个新建立的连接还没发数据过来,现在就接收的话程序就报错了,
#所以要想实现这个客户端发数据来时server端能知道,就需要让select再监测这个conn
msg_dic[conn] = queue.Queue() #初始化一个队列,后面存要返回给这个客户端的数据
else: #conn2
data = r.recv(1024)
print("收到数据",data)
msg_dic[r].put(data)
outputs.append(r) #放入返回的连接队列里
# r.send(data)
# print("send done....")
for w in writeable: #要返回给客户端的连接列表
data_to_client = msg_dic[w].get()
w.send(data_to_client) #返回给客户端源数据
outputs.remove(w) #确保下次循环的时候writeable,不返回这个已经处理完的连接了
for e in exceptional:
if e in outputs:
outputs.remove(e)
inputs.remove(e)
del msg_dic[e]
Python Day10的更多相关文章
-
python day10 函数(第二篇)
2019.4.10 S21 day10笔记总结 一.内容回顾 1.面试题相关: 1.py2和py3的区别 2.运算符的计算 :3 or 9 and 8 3.字符串的反转 4.is和==的区别 5.v1 ...
-
python day10 参数,命名空间
一.函数的参数--动态传参 1.动态接收位置参数 1.1在参数位置编写*表示接收任意多个内容 def chi(*food): print("我要吃", food) chi(&quo ...
-
python day- 10 动态参数 函数的嵌套 命名空间和作用域 global和nolocal
一.动态参数: 动态参数是形参的一类 分为:动态位置参数(* + 函数名)表示 调用后返回的是元祖 动态关键字参数(** + 函数名)表示 形参的排列顺序: 位置参数 > 动态位置参 ...
-
python day10: 反射补充,面向对象
目录 pythdon day 10 1. 反射补充 16. 面向对象 16.1 面向对象初步介绍 16.2 面向对象和面向过程区别 16.3 对象的进化 17. 类class 17.1 类的定义 17 ...
-
Python—day10 函数的参数分类
一.实参与形参 1.形参:定义函数,在括号内声明的变量名,用来接受外界传来的值 def fn(形参们): pass def fn(a,b,c):# 形参就是考虑实参的值, ...
-
python day10作业答案
2.def func(*args): sum = 0 for i in args: sum=sum+int(i) return sum a=func(2,3,9,6,8) print(a) 3. a= ...
- python day10作业
-
python day10 数据库(mysql基础)
一.数据库的概念 数据:事物的特征 数据库的本质是:通过套接字进行通信,来读存数据的一种软件,由于每次开发人员写程序都得写数据的套接字,所以诞生了数据库这个软件,减少重复劳动.(sql语句通用) 数据 ...
-
Python day10 global关键字、函数递归、匿名函数、map函数的用法详解
1.global关键字 引用全局变量,在局部全局变量改变,也会改变,global相当于指针,将地址指向全局变量的name name='littlepage' def littepage(): glob ...
随机推荐
-
react-native疑难
{"message":"TransformError: E:\\study\\react_native-workspace\\AwesomeProject\\node_m ...
-
HTML5和css3的总结二
继续接着昨天的整理 [倒影]:用的不是很多 -webkit-box-reflect:below 20px -webkit-linear-gradient(rgba(0,0,0,0,),rgba(0,0 ...
-
NYOJ-214 单调递增子序列(二) TLE 分类: NYOJ 2014-01-28 22:57 171人阅读 评论(0) 收藏
#include<stdio.h> #include<stdlib.h> #define max(x,y) x>y?x:y #define MAXX 100005 int ...
-
JS中的异步以及事件轮询机制
一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步 ...
-
Mybatis-no getter for property named &#39;col_name&#39; in &#39;class com.xxx.onebean&#39;
Mybatis中出现该异常 There is no getter for property named 'col_name' in 'class com.xxx.onebean 意思是onebean这 ...
-
深圳奥特迅现金流量——RESSET数据库
现金流不好,很多年都是负数.到公司官方网站上收集信息,拳头产品有矩阵柔性充电堆,主要盈利产品有电源和电源管理系统.还承担了深圳市的充电桩交钥匙项目. 下面是它这些年的股价,在大牛市的时候也上升至五十几 ...
-
java中比较特殊的三个浮点数Infinity、-Infinity、NaN
学过javaScript的应该都知道,在js中的数值型number类型中有几个特殊的数,一个正无穷大.一个负无穷大.一个不是一个数NaN. 后来无意中发现java中也有这三个数,不过这三个数是浮点数, ...
-
mysql trigger
转自:https://www.cnblogs.com/zyshi/p/6618839.html 阅读目录 什么是触发器 特点及作用 例子:创建触发器,记录表的增.删.改操作记录 弊端 什么是触发器 简 ...
-
mock.js的使用
安装 npm install mockjs 使用 const Mock = require("mockjs") //格式"属性名|min-max": " ...
-
【坚持】Selenium+Python学习之从读懂代码开始 DAY5
2018/05/22 函数作为返回值 [来源:廖雪峰的官方网站](https://www.liaoxuefeng.com/) #No.1 def lazy_sum(*args): def sum(): ...