大纲:
1、paramiko模块
2、开启进程的两种方式
3、多进程实现并发的套接字通信
4、Process对象的join方法和其他属性或方法
5、守护进程
6、同步锁
7、进程之间通信
8、生产者消费者模型
9、进程池已经
10、进程池之回调函数
一、paramiko模块
paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。
下载安装:pip3 install paramiko #在python3中
pycrypto,由于 paramiko 模块内部依赖pycrypto,所以先下载安装pycrypto #在python2中
pip3 install pycrypto
pip3 install paramiko
注:如果在安装pycrypto2.0.1时发生如下错误
command 'gcc' failed with exit status 1...
可能是缺少python-dev安装包导致
如果gcc没有安装,请事先安装gcc
ssh的使用
SSHClient
#基于用户名密码连接:
# import paramiko
#
# 创建SSH对象
# ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# # 连接服务器
#
# while True:
# ssh.connect(hostname='10.0.0.20', port=22, username='root', password='123456')
#
# cmd=input('>>').strip()
# # 执行命令
# stdin, stdout, stderr = ssh.exec_command(cmd)
# # 获取命令结果
# result = stdout.read()
# print(result.decode('utf-8'),end='')
# # 关闭连接
# ssh.close()
基于公钥密钥连接:
客户端文件名:id_rsa
服务端必须有文件名:authorized_keys(在用ssh-keygen时,必须制作一个authorized_keys,可以用ssh-copy-id来制作)
# import paramiko
#
# private_key = paramiko.RSAKey.from_private_key_file('id_rsa')
#
# # 创建SSH对象
# ssh = paramiko.SSHClient()
# # 允许连接不在know_hosts文件中的主机
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#
# while True:
# # 连接服务器
# ssh.connect(hostname='10.0.0.20', port=22, username='root', pkey=private_key)
#
# cmd = input('>>').strip()
# # 执行命令
# stdin, stdout, stderr = ssh.exec_command(cmd)
# # 获取命令结果
# result = stdout.read()
# print(result.decode('utf-8'),end='')
# # 关闭连接
# ssh.close()
SFTPClient
用于连接远程服务器并执行上传下载
基于用户名密码上传下载
################ SFTPClient
# import paramiko
#
# transport = paramiko.Transport(('10.0.0.20', 22))
# transport.connect(username='root', password='123456')
#
# sftp = paramiko.SFTPClient.from_transport(transport)
# # 将location.py 上传至服务器 /tmp/test.py
# sftp.put('id_rsa', '/tmp/id_rsa_test')
# # 将remove_path 下载到本地 local_path
# sftp.get('/tmp/AABB', 'AABB_test')
#
# transport.close()
基于用户名密码上传下载
基于公钥密钥上传下载
# import paramiko
#
# private_key = paramiko.RSAKey.from_private_key_file('id_rsa')
#
# transport = paramiko.Transport(('10.0.0.20', 22))
# transport.connect(username='root', pkey=private_key )
#
# sftp = paramiko.SFTPClient.from_transport(transport)
# # 将location.py 上传至服务器 /tmp/test.py
# sftp.put('id_rsa', '/tmp/test.py')
# # 将remove_path 下载到本地 local_path
# sftp.get('/tmp/88', '8888')
#
# transport.close()
# 基于公钥密钥上传下载
二、开启进程的两种方式
1、使用Process来启动进程
from multiprocessing import Process
import os,sys,time
def work(name):
print('task is running >%s' %name)
time.sleep(2)
if __name__=='__main__':
# Process(target=work,args=('sheng',), kwargs={'name':'sheng'})
p1=Process(target=work, args=('sheng',)) #在windows 下Process一定要写在__main__下面
p2= Process(target=work, args=('lele',))
p1.start()
p2.start()
print('主进程')
2、继承Process类来启动多进程
可以定义自己需要的属性。
from multiprocessing import Process
import os,sys,time
class Myprocess(Process):
def __init__(self,name):
super().__init__() #使用super可以防止继承的勒种有相同的属性
self.name=name
def run(self):
print('task is running >%s' % self.name)
time.sleep(2)
if __name__=='__main__':
p=Myprocess('sheng')
p.start()
print('主进程')
三、多进程实现并发的套接字通信
multiprocessing模块介绍
python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。
Process类的介绍:
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
1、group参数未使用,值始终为None
2、target表示调用对象,即子进程要执行的任务
3、args表示调用对象的位置参数元组,args=(1,2,'egon',)
4、kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
5、name为子进程的名称
方法介绍:
1、p.start():启动进程,并调用该子进程中的p.run()
2、p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
3、p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
4、p.is_alive():如果p仍然运行,返回True
5、p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
Process类的使用
注意:在windows中Process()必须放到# if __name__ == '__main__':下
服务端:
# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import *
from multiprocessing import Process
import os,sys
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('127.0.0.1',8080))
s.listen(5)
def work(conn,addr):
print('===>进程号:%s'%os.getpid())
while True:
try:
data=conn.recv(1024)
if not data:continue
conn.send(data.upper())
except Exception as e:
break
conn.close()
if __name__=='__main__':
while True:
conn,addr=s.accept()
p=Process(target=work,args=(conn,addr))
p.start()
s.close
View Code
客户端:
# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import *
c=socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8080))
while True:
msg=input('>>').strip()
if not msg:continue
c.send(msg.encode('utf-8'))
data=c.recv(1024)
print(data.decode('utf-8'))
c.close()
View Code
四、Process对象的其他属性与join方法
属性介绍:
1、p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2、p.name:进程的名称
3、p.pid:进程的pid
4、p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
5、p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
例子:
from multiprocessing import Process
import os,sys,time
def work(name):
print('task is running >%s' %name)
time.sleep(1)
if __name__=='__main__':
# Process(target=work,args=('sheng',), kwargs={'name':'sheng'})
p1=Process(target=work, args=('sheng',)) #在windows 下Process一定要写在__main__下面
p1.start()
p1.terminate() #终止p1进程
time.sleep(1)
print(p1.is_alive()) #查看p1进程是否存在
print(p1.name) #Process-1 查看进程名字
print(p1.pid)
print('主进程',os.getpid())
join函数等待主进程结束后在执行主函数。
from multiprocessing import Process
import os,sys,time
def work(name):
print('task is running >%s' %name)
time.sleep(1)
if __name__=='__main__':
# Process(target=work,args=('sheng',), kwargs={'name':'sheng'})
p1=Process(target=work, args=('sheng',)) #在windows 下Process一定要写在__main__下面
p2= Process(target=work, args=('lele',))
p1.start()
p2.start()
p1.join() #等待子进程执行完,在执行主进程
p2.join()
print('主进程')
五、守护进程
主进程创建守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
例子1:
from multiprocessing import Process
import os,sys,time
def work(name):
print('task is running >%s' %name)
time.sleep(10)
if __name__=='__main__':
# Process(target=work,args=('sheng',), kwargs={'name':'sheng'})
p1=Process(target=work, args=('sheng',)) #在windows 下Process一定要写在__main__下面
p1.daemon =True #p1.start开始之前设置daemon,
p1.start()
time.sleep(0.5)
print('主进程',os.getpid())
# 特点:
#1、子进程在主进程结束后结束子进程
#2、子进程下面不能产生子进程
例子2:
#主进程代码运行完毕,守护进程就会结束
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")
if __name__=='__main__':
p1=Process(target=foo)
p2=Process(target=bar)
p1.daemon=True
p1.start()
p2.start()
print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止
# 迷惑人的例子
六、同步锁
例子1:
import os,time
from multiprocessing import Process,Lock
def work(name,mutex):
mutex.acquire() #锁开始
print('%s task is running %d' %(name,os.getpid()))
time.sleep(2)
print('%s=> is done %d'%(name,os.getpid()))
mutex.release() #锁结束
if __name__=='__main__':
mutex = Lock()
p1=Process(target=work,args=('sheng',mutex))
p2=Process(target=work,args=('leqi',mutex))
p1.start()
p2.start()
print('===>主进程')
例子2:
import json
import os
import time
from multiprocessing import Process,Lock
def search():
dic=json.load(open('db.txt'))
print('\033[32m[%s] 看到剩余票数<%s>\033[0m' %(os.getpid(),dic['count']))
def get_ticket():
dic = json.load(open('db.txt'))
time.sleep(0.5) #模拟读数据库的网络延迟
if dic['count'] > 0:
dic['count']-=1
time.sleep(0.5) # 模拟写数据库的网络延迟
json.dump(dic,open('db.txt','w'))
print('\033[31m%s 购票成功\033[0m' %os.getpid())
def task(mutex):
search()
mutex.acquire()
get_ticket()
mutex.release()
if __name__ == '__main__':
mutex=Lock()
for i in range(3):
p=Process(target=task,args=(mutex,))
p.start()
#例子:模拟抢票
七、进程之间通信
1、共享内存:
启动进程来减同一个内存数据:
from multiprocessing import Process,Manager,Lock
def work(dic):
dic['count']-=1
if __name__=='__main__':
m=Manager()
dic=m.dict({'count':100}) #共享字典dic,
print(dic)
p_l=[]
for i in range(100):
p=Process(target=work,args=(dic,))
p_l.append(p)
p.start()
for p in p_l:
p.join()
print(dic)
# 结果:
# {'count': 100}
# {'count': 2}
# 子进程同时修改共享字典dic,
View Code
使用加锁的机制:控制资源的抢占保证数据的正确性。
from multiprocessing import Process,Manager,Lock
def work(dic,mutex):
mutex.acquire()
dic['count']-=1
mutex.release()
if __name__=='__main__':
mutex=Lock()
m=Manager()
dic=m.dict({'count':100}) #共享字典dic,
print(dic)
p_l=[]
for i in range(100):
p=Process(target=work,args=(dic,mutex))
p_l.append(p)
p.start()
for p in p_l:
p.join()
print(dic)
# 结果
# {'count': 100}
# {'count': 0}
# 自己处理加锁的方式。
#缺点降低了运行的效率、
View Code
2、队列
from multiprocessing import Queue
q=Queue(4)
q.put('fist')
q.put('scond')
q.put('third') #put放入队列
q.put('fourth',block=True) #队列满了就抛异常(不卡)
print(q.get())#get取出队列
print(q.get())
print(q.get())
View Code
八、生产者消费者模型
1、队列的应用
from multiprocessing import Process,Lock,Queue
import time,os
def producer(q):
'''生产者程序'''
for i in range(10):
res='包子 %s' %i
q.put(res)
time.sleep(0.7)
print('< %s生产了[%s]>' %(os.getpid(),res))
def consumer(q):
'''消费者程序'''
while True:
res=q.get()
time.sleep(1)
print('< %s消费了[%s]>' %(os.getpid(),res))
if __name__=='__main__':
q=Queue()
p1=Process(target=producer,args=(q,))
c1=Process(target=consumer,args=(q,))
# 启动生产者
p1.start()
# 启动消费者
c1.start()
print('==>main')
上个例子中有问题:
生产者p1循环完之后,已经结束生产者进程,消费者c1一直等待就会卡住。消费者无法得知生产者什么时候完成。
解决方法:
from multiprocessing import Process, JoinableQueue
import time, os
def producer(q, name):
for i in range(3):
time.sleep(2)
res = '%s%s' % (name, i)
q.put(res)
print('\033[45m<%s> 生产了 [%s]\033[0m' % (os.getpid(), res))
q.join() #生产者等待q的结束之后在结束(q的结束就是q为空的时候)
def consumer(q):
while True:
res = q.get()
# time.sleep(1.5)
print('\033[34m<%s> 吃了 [%s]\033[0m' % (os.getpid(), res))
q.task_done()
if __name__ == '__main__':
q = JoinableQueue()
# 生产者们:即厨师们
p1 = Process(target=producer, args=(q, '包子'))
p2 = Process(target=producer, args=(q, '饺子'))
p3 = Process(target=producer, args=(q, '馄饨'))
# 消费者们:即吃货们
c1 = Process(target=consumer, args=(q,))
c2 = Process(target=consumer, args=(q,))
c1.daemon=True
c2.daemon=True #把消费者设置为守护进程当主进程结束之后同时结束
p1.start()
p2.start()
p3.start()
c1.start()
c2.start()
p1.join() #主进程等待p1的结束之后继续执行后面
print('主')
View Code
九、进程池
1、进程池的使用:
import os,time
from multiprocessing import Pool
def work(n):
print('task is running >%s' %os.getpid())
time.sleep(1)
return n**2
if __name__=='__main__':
p=Pool()
# for i in range(10):
# res=p.apply(work,args=(i,)) #同步执行,等待任务执行完毕
# print(res)
res_l = []
for i in range(10):
res=p.apply_async(work,args=(i,)) #异步执行
res_l.append(res)
p.close() #不允许往进程池提交新的任务
p.join() #等待进程池的任务全部完成
for res in res_l:
print(res.get()) #取出进程的返回值
2、进程池控制并发的套接字通信
服务端:
# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import *
from multiprocessing import Pool
import os,sys
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('127.0.0.1',8080))
s.listen(5)
def work(conn,addr):
print('===>进程号:%s'%os.getpid())
while True:
try:
data=conn.recv(1024)
if not data:continue
conn.send(data.upper())
except Exception as e:
break
conn.close()
if __name__=='__main__':
p=Pool(3)
while True:
conn,addr=s.accept()
p.apply_async(work,args=(conn,addr))
s.close
#服务端:
客户端:
# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import *
c=socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8080))
while True:
msg=input('>>').strip()
if not msg:continue
c.send(msg.encode('utf-8'))
data=c.recv(1024)
print(data.decode('utf-8'))
c.close()
#客户端
十、进程池之回调函数
回调函数的应用的例子:
当没有使用回调函数:如下
import requests,os,time
from multiprocessing import Pool
def get_page(url): #get网页内容的方法
print('<%s> get :%s' %(os.getpid(),url))
request=requests.get(url)
if request.status_code==200:
return {'url':url,'text':request.text} #返回一个字典
def parse_page(dic):
print('<%s> parse :%s' %(os.getpid(),res))
print('url:%s size :%s' %(dic['url'],len(dic['text'])))
if __name__=='__main__':
p = Pool(4)
urls=[
'https://www.baidu.com',
'https://www.python.org',
'http://www.sina.com.cn',
'https://www.openstack.org',
'https://about.gitlab.com'
]
res_l=[]
for url in urls:
res=p.apply_async(get_page,args=(url,)) #apply_async使用异步启动进程(get_page)
res_l.append(res)
p.close() #先close()保证没有新的进程添加到进程池
p.join() #等待钱需要close()
for res in res_l:
parse_page(res.get())
上面的例子中有问题: =》
效率问题:需要等待进程池里面的函数都执行完毕,才能继续执行主进程(当进程池所有的进程把网页下载完成后才能分析提取页面内容。)
使用回调函数来提高程序的效率。当进程池的一个进程把url的网页下载下来之后就开始分析提取此网页内容。
import requests,os,time
from multiprocessing import Pool
def get_page(url): #get页面函数
print('<%s> get :%s' %(os.getpid(),url))
request=requests.get(url)
if request.status_code==200:
return {'url':url,'text':request.text}
def parse_page(dic): #分析页面内容函
print('<%s> parse: %s' %(os.getpid(),dic['url']))
print('url:%s size :%s' %(dic['url'],len(dic['text'])))
if __name__=='__main__':
p = Pool(4) #数值4是意味可以并发4个进程,不写默认是系统cpu内核数
urls=[
'https://www.baidu.com',
'https://www.python.org',
'http://www.sina.com.cn',
'https://www.openstack.org',
'https://about.gitlab.com'
]
res_l=[]
for url in urls:
p.apply_async(get_page,args=(url,),callback=parse_page) #通知主进程回调parse_page函数 使用回调函数
p.close()
p.join()
运行结果:
# <30700> get :https://www.baidu.com
# <26740> get :https://www.python.org
# <10516> get :http://www.sina.com.cn
# <10516> get :https://www.openstack.org
# <29920> parse: http://www.sina.com.cn
# url:http://www.sina.com.cn size :600911
# <29780> get :https://about.gitlab.com
# <29920> parse: https://www.baidu.com
# url:https://www.baidu.com size :2443
# <29920> parse: https://www.python.org
# url:https://www.python.org size :48708
# <29920> parse: https://about.gitlab.com
# url:https://about.gitlab.com size :81129
# <29920> parse: https://www.openstack.org
# url:https://www.openstack.org size :60365