python的进程和线程_python之进程和线程3

1 multiprocessing模块

(1.)直接导入

from multiprocessing import Process

import os

import time

def info(name):

print("name:",name)

print('parent process:', os.getppid())

print('process id:', os.getpid())

print("------------------")

def foo(name):

info(name)

time.sleep(50)

if __name__ == '__main__':

info('main process line')

p1 = Process(target=info, args=('alvin',))

p2 = Process(target=foo, args=('egon',))

p1.start()

p2.start()

p1.join()

p2.join()

print("ending")

time.sleep(100)

>>

name: main process line

parent process: 16976

process id: 18456

------------------

name: alvin

parent process: 18456

process id: 19884

------------------

name: egon

parent process: 18456

process id: 19112

------------------

ending

(2.)创建类的方法

构造方法:

Process([group [, target [, name [, args [, kwargs]]]]])

group: 线程组,目前还没有实现,库引用中提示必须是None;

target: 要执行的方法;

name: 进程名;

args/kwargs: 要传入方法的参数。

实例方法:

is_alive():返回进程是否在运行。

join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

start():进程准备就绪,等待CPU调度

run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

terminate():不管任务是否完成,立即停止工作进程

属性:

daemon:和线程的setDeamon功能一样

name:进程名字。

pid:进程号。

2 协程

协程的优点:

(1)     由于单线程不存在切换

(2)     不再有任何锁的概念

yield是最基本的携程函数

没有办法监听到IO,进行切换

可以保存到数据的状态通过send方法来运行

import time

# 注意到consumer函数是一个generator(生成器):

# 任何包含yield关键字的函数都会自动成为生成器(generator)对象

def consumer():

r = ''

while True:

n = yield r

if not n:

return

print('[CONSUMER] ←← Consuming %s...' % n)

time.sleep(1)

r = '200 OK'

def produce(c):

# 1、首先调用c.next()启动生成器

next(c)

n = 0

while n < 5:

n = n + 1

print('[PRODUCER] →→ Producing %s...' % n)

# 2、然后,一旦生产了东西,通过c.send(n)切换到consumer执行;

cr = c.send(n)

# 4、produce拿到consumer处理的结果,继续生产下一条消息;

print('[PRODUCER] Consumer return: %s' % cr)

# 5、produce决定不生产了,通过c.close()关闭consumer,整个过程结束。

c.close()

if __name__=='__main__':

# 6、整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

c = consumer()

produce(c)

greenlet模块

可以实现手动切换

调用属性swich

gevent可以实现IO的监听

gevent.joinall 开启所有程序

gevent.spawn 切换

3 IO模型

IO指input, output

IO发生时涉及的对象和步骤

会涉及到两个系统对象,一个是调用这个IO的process(or thread),另一个就是系统内核(kernel)。当一个操作发生时,会经历两个阶段:

(1)     等待数据准备

(2)     将数据从内核拷贝到进程中

IO模型类型:

1.阻塞  IO

035f9d9a0978cd90850c40dfc82879a7.png

1.非阻塞 IO

非阻塞IO:发送多次系统调用

优点:wait for data无阻塞

缺点:系统调用太多

不能及时拿到数据

两个阶段:wait for data非阻塞

copy data 阻塞

b23e9b06ce3c3e7ab3c41614ce2ffdf3.png

非阻塞的recvform系统调用调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error。进程在返回之后,可以干点别的事情,然后再发起recvform系统调用。重复上面的过程,循环往复的进行recvform系统调用。这个过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。

1.IO多路复用(监听多个链接)

特点:(1)全程阻塞

能监听多个文件描述符  实现并发

f37765648ad4ead36516e7a787feeb61.png

#服务端

import select

import socket

sock=socket.socket()#产生一个套接字

sock.bind(("127.0.0.1",8080))

sock.listen(5)

sock.setblocking(False)

inputs=[sock,]

while 1:

r,w,e=select.select(inputs,[],[])#监听有变化的套接字sock

#wait for data

for obj in r:

if obj==sock:

conn,addr=obj.accept()#从内核copy信息到用户态

print("conn",conn)

inputs.append(conn)#监听列表添加客户conn

else:

data=obj.recv(1024)#接收信息

print(data.decode("utf8"))

send_data=input(">>")#发送信息

obj.send(send_data.encode("utf8"))

#客户端

import socket

sock=socket.socket()

sock.connect(("127.0.0.1",8080))

while 1:

data=input("input>>")

sock.send(data.encode("utf8"))

recv_data=sock.recv(1024)

print(recv_data.decode("utf8"))

sock.close()

对于文件描述符(套接字对象)

(1)    是一个非零整数,不会变

(2)    收发数据的时候,对于接收端而言,数据先到内核空间,然后copy到用户空间,同时,内核空间数据清除

1.异步IO

全程无阻塞

04b1a9b85b4bbe7e72eff7c283ee3cdd.png

5.驱动信号

小结:

29e9ac747ba1097090270963986b28a8.png

有阻塞blocking

无阻塞non-blocking

调用blocking IO会一直block住对应的进程知道操作完成

non-blocking IO在kernel还准备数据的情况下会立刻返回

有阻塞是同步阻塞:阻塞非阻塞  IO多路复用

无阻塞是异步阻塞:异步IO

4 selectors模块

IO多路复用实现机制

Win:select

Linux:select,poll,epoll

Select缺点:1.每次调用select都要将所有的fd(文件描述符)拷贝到内核空间,导致效率下降

2.遍历所有的fd,是否有数据访问(最重要的问题)

3.最大连接数(1024)

poll:最大连接数没有限制

epoll:1.第一个函数创建epoll句柄,将所有的fd(文件描述符)拷贝到内核空间

只需要拷贝一次

2.回调函数:某一个函数或者某一个动作成功完成之后会触发的函数

为所有的fd绑定一个回调函数,但有数据访问触发该回调函数

回调函数将fd放到列表中

import selectors

import socket

sock=socket.socket()

sock.bind(("127.0.0.1",8080))

sock.listen(5)

sock.setblocking(False)

sel=selectors.DefaultSelector()#根据具体平台选择最佳IO多路机制

def read(conn,mask):

try:

data=conn.recv(1024)

print(data.decode("utf8"))

data2=input(">>")

conn.send(data2.encode("utf8"))

except Exception:

sel.unregister(conn)

def accept(sock,mask):

sel.register(sock,selectors.EVENT_READ,accept)

conn,addr=sock.accept()

sel.register(conn,selectors.EVENT_READ,read)

sel.register(sock,selectors.EVENT_READ,accept)#注册功能

while 1:

events=sel.select()

for key,mask in events:

print(key.data)#定义的函数

print(key.fileobj)#socket对象

func=key.data

obj=key.fileobj

func(obj,mask)

break

import socket

sock=socket.socket()

sock.connect(("127.0.0.1",8080))

while 1:

data=input("input>>")

sock.send(data.encode("utf8"))

recv_data=sock.recv(1024)

print(recv_data.decode("utf8"))

sock.close()

5. 队列

队列用在多线程,多进程中,用来保护数据

队列是个数据类型

优点:线程安全

import queue

q=queue.Queue(3)#默认是先进先出

q.put(111)

q.put("hello")

q.put(222)

print(q.get())

print(q.get())

print(q.get())

>>

111

hello

222

import queue

q=queue.Queue(3)#默认是先进先出

q.put(111)

q.put("hello")

q.put(222)

q.put(223,False)#q=queue.Queue(3)队列定义只能放3个值,

# #超过限额时,返回错误信息

print(q.get())

print(q.get())

print(q.get())

q.get()#没有数据的时候不会报错,只会等待

q.get(False)#数据为空,报错

先进后出

import queue

q=queue.LifoQueue()

q.put(111)

q.put(5)

q.put(43)

print(q.get())

优先级

import queue

q=queue.PriorityQueue()

q.put([4,"hello"])

q.put([1,"hello5"])

print(q.get())

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值