在这里,我们将给大家分享关于python主机批量管理脚本的知识,让您更了解下的本质,同时也会涉及到如何更有效地(六)Python基础,字符串(下)、C#调用python脚本(二)python代码打包成
在这里,我们将给大家分享关于python主机批量管理脚本的知识,让您更了解下的本质,同时也会涉及到如何更有效地(六)Python基础,字符串(下)、C#调用python脚本(二)python代码打包成库供C#调用、Python 基础总篇(下)选择结构开始!、Python 多进程(下)和多线程的内容。
本文目录一览:- python主机批量管理脚本(下)(python批量管理远程服务器)
- (六)Python基础,字符串(下)
- C#调用python脚本(二)python代码打包成库供C#调用
- Python 基础总篇(下)选择结构开始!
- Python 多进程(下)和多线程
python主机批量管理脚本(下)(python批量管理远程服务器)
接着上节生成的配置文件信息,我们要实现根据配置文件内容实现主机的批量管理。
功能亮点:
(1)支持raw_iuput列退格重新编辑
(2)日志显示加亮
(3)使用多进程提升执行效率
使用效果图:
实现代码如下:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#update:20161222
#auth:dsc
#description:批量管理工具
import os,paramiko,sys,time,ConfigParser
from multiprocessing import Process,Pool
import base64
import readline
'''''' 返回命令列表 ''''''
_BACK_CMD_LIST = (''quit'',''q'',''exit'',''bye'')
'''''' 不作为命令列表 ''''''
_DO_NOTHING_LIST = ('''',''\n'',''\r'',None)
'''''' 禁用命令列表 ''''''
_FORBDI_CMD = (''more'',''less'',''vi'')
'''''' 进程池大小 ''''''
PROCESSES = 30
'''''' 文件分发基础目录 ''''''
DISTRIBUTE_BASE_DIR = ''/tmp''
'''''' 初始化配置文件 ''''''
class InitConf:
def __init__(self,conf_file):
try:
file_path = os.path.join(sys.path[0],conf_file)
self.cf = ConfigParser.SafeConfigParser()
self.cf.read(conf_file)
except Exception as e:
Log(''RED'',''ConfigParserError:%s'' % (str(e)))
sys.exit(1)
'''''' 读取文件配置信息 ''''''
def readConf(self):
hosts=[]
try:
opts = eval(self.cf.get(''host'',''connList''))
for opt in opts:
host = []
s = eval(self.cf.get(''information'',opt))
host.append(s[''ip''])
host.append(s[''user''])
host.append(base64.b64decode(s[''password'']))
host.append(s[''port''])
hosts.append(host)
except Exception as e:
Log(''RED'',''ReadConfError:%s'' % (str(e)))
sys.exit(1)
return hosts
'''''' 初始化ssh服务 ''''''
def init_server():
server = paramiko.SSHClient()
server.load_system_host_keys()
server.set_missing_host_key_policy(paramiko.AutoAddPolicy())
return server
'''''' 执行命令 ''''''
def exec_pools_cmd(server,server_list,action,cargs):
start = time.time()
p = Pool(processes = PROCESSES)
for h in server_list:
if action == ''ssh_run'':
p.apply_async(ssh_run,[h,] + cargs)
elif action == ''distribute_file'':
p.apply_async(distribute_file,[h,] + cargs)
p.close()
p.join()
end = time.time()
print ''\033[31;1mCost time:%ss\033[0m'' % str(end - start)
'''''' 初始化日志目录 ''''''
def chkLogs():
if not os.path.exists(os.path.join(sys.path[0],''log'')):
os.makedirs(os.path.join(sys.path[0],''log''))
'''''' 获取用户输入 ''''''
def user_input(str):
try:
cmd = raw_input(''\033[32;0m[%s]=>\033[0m'' %(str)).strip()
return cmd
except KeyboardInterrupt:
Log(''NOMAL'',''\nKeyboardInterrupt'')
return None
except Exception:
print ''\n''
sys.exit(1)
'''''' 定义日志输出格式 ''''''
def Log(type,msg):
date_detail = time.strftime(''%Y_%m_%d %H:%M:%S'')
logText=''[%s] %s'' %(date_detail,msg)
if type == ''NOMAL'':
print ''\033[32;1m%s\033[0m'' %(msg)
elif type == ''GREEN'':
print ''\033[32;1m[INFO ] %s\033[0m'' %(logText)
elif type == ''RED'':
print ''\033[31;1m[ERROR] %s\033[0m'' %(logText)
elif type == ''YELLOW'':
print ''\033[33;1m[WARN ] %s\033[0m'' %(logText)
'''''' 操作日志记录 ''''''
def log_write(uargs):
date_detail = time.strftime(''%Y_%m_%d %H:%M:%S'')
log = ''Time:%s | Type:%s | Detial:%s | Server:%s | Result:%s\n'' % (date_detail,uargs[0],uargs[1],uargs[2],uargs[3])
with open(''./log/user_%s_record.log'' % (time.strftime(''%Y_%m_%d'')),''a+'') as f:
f.write(log)
'''''' 命令执行函数 ''''''
def ssh_run(host_info,cmd,server):
try:
ip,username,password,port= host_info[0],host_info[1],host_info[2],host_info[3]
server.connect(ip,int(port),username,password,timeout=5)
stdin,stdout,stderr = server.exec_command(cmd)
cmd_result = stdout.read(),stderr.read()
for rs in cmd_result:
for line in rs.split(''\n''):
if line.strip() == '''':
continue
else:
Log(''NOMAL'',''[%s]: %s'' %(ip,line))
except Exception as e:
log_write([''cmd batch'',cmd,ip,''failed''])
Log(''RED'',''[%s]: %s'' % (ip,str(e)))
else:
log_write([''cmd batch'',cmd,ip,''success''])
'''''' 文件分发函数 ''''''
def distribute_file(host_info,file_name):
try:
ip,uname,passwd,port = host_info[0],host_info[1],host_info[2],int(host_info[3])
RemoteFile = os.path.join(DISTRIBUTE_BASE_DIR,os.path.basename(file_name))
t = paramiko.Transport((ip,int(port)))
t.connect(username=uname,password=passwd)
sftp = paramiko.SFTPClient.from_transport(t)
sftp.put(file_name,RemoteFile)
t.close()
except Exception as e:
log_write([''distribute file'',file_name,ip,''failed''])
Log(''RED'',''[%s] : %s'' % (ip,str(e)))
else:
log_write([''distribute file'',file_name,ip,''success''])
Log(''GREEN'',''Distribute %s to %s:%s Successfully!'' % (file_name,ip,RemoteFile))
'''''' 主函数 ''''''
def main(conf_name):
p = InitConf(conf_name)
server_list = p.readConf()
server = init_server()
while True:
print ''''''
-------------------------
[1]Execute command.
[2]Distribute files.
[q]Exit.
-------------------------\n''''''
choice = user_input(''Choice'')
if choice == ''1'':
while True:
cmd = user_input(''Cmd'')
if cmd in _BACK_CMD_LIST:
break
elif cmd in _DO_NOTHING_LIST:
continue
elif (set(_FORBDI_CMD) & set(cmd.split())) != set([]):
'''''' 禁止使用命令列表 ''''''
Log(''RED'',''Can not use %s command!'' %(str(_FORBDI_CMD)))
continue
exec_pools_cmd(server,server_list,''ssh_run'',[cmd,server])
elif choice == ''2'':
while True:
file_name = user_input(''Distribute'')
start = time.time()
if file_name in _BACK_CMD_LIST:
break
elif file_name == None:
continue
file_chcek = os.path.isfile(file_name)
if file_chcek == False:
Log(''YELLOW'',''The file does not exist or it is a directory!'')
continue
exec_pools_cmd(server,server_list,''distribute_file'',[file_name,])
elif choice in _BACK_CMD_LIST:
server.close()
sys.exit()
else:
continue
'''''' 程序入口 ''''''
if __name__ == ''__main__'':
if len(sys.argv) < 2:
Log(''RED'',''Usage: python %s test.conf'' %(sys.argv[0]))
sys.exit(1)
else:
os.system(''clear'')
conf_name = sys.argv[1]
main(conf_name)
(六)Python基础,字符串(下)
#字符串:
##首字母大写,其他位置字母小写
# test = "QSGlhqQSG"
# v=test.capitalize()
# print(v)
#把所有变小写,casefold()更加牛逼,很多未知的相对应的变小写,lower()功能相对于较小,只适应于英文字母大小写。
# v1=test.casefold()
# print(v1)
# v2=test.lower()
# print(v2)
# 占用20字节位置,把字符串放在中间,后面的参数用来填充剩余的空白。--空白位置填充,只能一个字符串,可有可无
# v3=test.center(20,"中")
# print(v3)
# test = 'qsglhq'
# v1 = test.ljust(20,'*') #字符串靠左
# v2 = test.rjust(20,'*') #字符串靠右
# v3 = test.zfill(20) #字符串靠右,只能用0填充
# print(v1,v2,v3)
#去字符串中寻找,寻找子序列的出现次数。
# v = test.count("lh",3,5)
# print(v)
# v = test.encode()
#
# v = test.decode()
#以什么开始,以什么结束
# v = test.endswith('SG')
# print(v)
# v1 = test.startswith('QS')
# print(v1)
#start和end参数,start位为子字符串第一个字符,end位为子字符串最后一个字符再往后移动一位。
# v2 = test.startswith('lhq',3,6)
# print(v2)
# v3 = test.endswith('qQ',3,7)
# print(v3)
#\t为空格标签,从左往右数,每6个一组,不够的用空格补,刚好6位用6空格,参数是整形定义填充多少位,默认8位。
# test1 = "1\t234567\t89"
test2 = "name\temail\tpass\nlhq\t18877490764.163.com\tlhq960305\nlhq\t18877490764.163.com\tlhq960305\nlhq\t18877490764.163.com\tlhq960305\n" \
# "lhq\t18877490764.163.com\tlhq960305\n"
# s1 = test1.expandtabs(6)
s2 = test2.expandtabs(25)
# print(s1,len(s1)) ####1 234567 89 20####
print(s2)
#从前往后找,找到第一个匹配子字符串之后,获取其第一个字符的位置,
#start与end参数:7<=e<9 start定义的是开区间,end定义的是闭区间。
# test = "alexalexa"
# e = test.find('xa',7,9)
# print(e) ####没找到是 -1
#相似方法:index 去区别在于index找不到程序报错。
#格式化,将一个字符串中的占位符替换成指定的值
# test = "i am {name}, age {a}"
# print(test)
# v = test.format(name = 'lhq',a = 25)
# print(v)
# # 类型二:使用顺序替换
# test = "i am {0}, age {1}"
# v = test.format('lhq',25)
# print(v)
#格式化,传入值以键值对的形式{'name':'lhq','a':25}
# test = "i am {name}, age {a}"
# v1 = test.format(name = 'lhq',a = 25)
# v2 = test.format_map({'name':'lhq','a':25})
# print(v1)
# print(v2)
#判断字符串中是否都是数字和字符串,结果以布尔形式呈现。
# test1 = "123asd!@#"
# test2 = "123asd"
# v1 = test1.isalnum()
# v2 = test2.isalnum()
# print(v1)
# print(v2)
#是否都是字母、汉字
# test = 'ascd'
# test = 'asA梁鸿倩cd'
# v = test.isalpha()
# print(v)
#判断当前是否是数字,isdigit 特别的数字也行 isnumeric 最牛
# test1 = '1234' #True True#
# test2 = '②' #False True#
#test3 = '二' #False False True
# v1 = test3.isdecimal() #十进制小数
# v2 = test3.isdigit()
# v3 = test3.isnumeric()
# print(v1,v2,v3)
#如果字符串是有效标识符,则 isidentifier() 方法返回 True,否则返回 False。
#如果字符串仅包含字母数字字母(a-z)和(0-9)或下划线(_),则该字符串被视为有效标识符。有效的标识符不能以数字开头或包含任何空格。
# txt = "Demo"
# x = txt.isidentifier()
# print(x)
#更多示范
# a = "MyFolder" #True
# b = "Demo002" #True
# c = "2bring" #False 不能以数字开头
# d = "my demo" #False 不能有空格
# print(a.isidentifier())
# print(b.isidentifier())
# print(c.isidentifier())
# print(d.isidentifier())
#大小写转换
# test = 'QSGlhq'
# v = test.swapcase()
# print(v)
# import keyword
# print(keyword.iskeyword('def'))
# test = 'def'
# v = test.isidentifier()
# print(v)
#是否存在不可显示的字符
#\t 制表符
#\n 换行符
# test = 'qeqww\nesa'
# v = test.isprintable()
# print(v)
#判断是否全部都是空格
# test = ' '
# v = test.isspace()
# print(v)
#判断是否是表标题,
# #标题里面每个单词首字母都是大写、
# test = 'Return True if the string is a title-cased string, False otherwise'
# # test = 'Return True'
# # v = test.istitle()
# # print(v)
#
# #把字符串转换成标题
# v = test.title()
# print(v)
#将字符串中的每一个元素安装指定分隔符进行拼接
# test = '你是风儿我是沙'
# t = ' '
# print(t.join(test)) #你 是 风 儿 我 是 沙
# print(' '.join(test)) #你 是 风 儿 我 是 沙
# print('*'.join(test)) #你*是*风*儿*我*是*沙
#判断是否是大小写,和大小写转换
# test = 'Qsglhq'
# # v1 = test.islower()
# # v2 = test.lower()
# # print(v1,v2)
# v1 = test.isupper()
# v2 = test.upper()
# print(v1,v2)
#去除左右空白,不能去除中间空白,\n去除换行,去除\t空白符,还可以去除指定字符
# test1 = ' qsglhq'
# test2 = '\nqsglhq\t'
# v1 = test1.lstrip()
# v2 = test1.rstrip()
# v3 = test1.strip()
# v4 = test2.strip()
# print(test2,v4)
# v5 = test1.rstrip('lhq')
# print(v5)
#maketrans 与 translate 协作,以前者规则替换字符串
# v = "lsahfoifsfsfseiufhwihweitfbgvnidgukljrw"
# m = str.maketrans("aeIoU",'ABCDE')
# new_v = v.translate(m)
# print(new_v)
#partition从左边分割,三部分,中间是分割子集的本身,
# test = "maketrkeakns"
# # v = test.partition('ke')
# # print(v)
# # #rpartition 从右边分,其他和partition一样。
# # v1 = test.rpartition('ke')
# # print(v1)
#
# #分割字符串,不带分割子集本身,不写分割次数默认全部分,rsplit从右边开始分。
# v = test.split('k')
# print(v) #['ma', 'etr', 'ea', 'ns'] 全分了
# v1 = test.split('k',2)
# print(v1) #['ma', 'etr', 'eakns'] 只分了两次
# v2 = test.rsplit('k',1)
# print(v2) #['maketrkea', 'ns']
# test = 'fsdhfr\ndwekufhwf\ndweiuhftopgj\ndwajdgh'
# v = test.splitlines()
# print(v) #['fsdhfr', 'dwekufhwf', 'dweiuhftopgj', 'dwajdgh']
# # True #带换行符
# v1 = test.splitlines(True) #['fsdhfr\n', 'dwekufhwf\n', 'dweiuhftopgj\n', 'dwajdgh']
# print(v1)
# # False 不带换行符
# v2 = test.splitlines(False)
# print(v2) #['fsdhfr', 'dwekufhwf', 'dweiuhftopgj', 'dwajdgh']
#以什么开始,以什么结尾
# test = 'kajhdfak.1.1.1'
# v = test.startswith('ka')
# print(v)
# v1 = test.endswith('.1')
# print(v1)
#替换字符串中的字符,后面带int参数是替换次数
# test = '123a456a789a0'
# v = test.replace('a','***')
# v1 = test.replace('a','##',2)
# print(v)
# print(v1)
##################七个基本魔法#############################
# join
# split
# find
# strip
# upper
# lower
#replace
####################四个灰魔法############################
#索引、下标、获取字符串中的某一个字符
# test = '覃世广和梁鸿倩'
# v = test[0]
# print(v)
# v1 = test[0:2] #0<= v1 <2 切片
# print(v1) #qs
# v2 = test[0:-1] #不要最后一个
# print(v2) #qsglh
#
# #获取字符串长度
# v = len(test)
# print(v)
#
# for ql in test:
# print(ql)
#四个灰魔法:索引、切片、for循环、len()
####################一个深灰魔法############################
##!字符串创建就不可以修改,一旦想修改或者拼接都是重新创建字符串
# for 循环中 break和continue也同样受用
# test = '覃世广12345657'
# for qin in test:
# print(qin)
# break
# for qin in test:
# continue
# print(qin)
#range获取连续的数字,后面加步长可以使其不连续。
# v = range(100)
# print(v)
# for p in v:
# print(p)
# v = range(0,100,5)
# for p in v:
# print(p)
# test = input(">>>")
# for i in test:
# v=test.find(i)
# print(v)
# s = input('>>>')
# num = len(s)
# v = range(0,num)
# for i in v:
# print(i,s[i])
C#调用python脚本(二)python代码打包成库供C#调用
python:
新建一个py文件“__init__.py”,简单测试
def main(): return 11111; def test1(name): return name;
把它扔到一个文件夹 例如 “mytest1”下,在mytest1外新建py文件“setup.py” ,内容:
from setuptools import find_packages,setup setup( name = 'mytest1', version = '0.1', packages = find_packages(), )
在setup.py路径内进入cmd, 运行 python setup.py install。 不报错的话直接就打包好了,可以在C#下import 《mytest1》,然后调用main或者test1方法。
以上只是我的简单测试方式,关于打包方式还有很多方式,比如先打包 python setup.py sdist 然后再安装 pip install xxx……。
C#:
根据本机的python版本选择引用的依赖包:pythonnet_netstaandard_py39_win(提示不支持net6就换个版本,vs的bug)
引用python的代码:
using (Py.GIL()) { dynamic np = Py.Import("numpy"); Console.WriteLine(np.cos(np.pi * 2)); dynamic ooo = Py.Import("mytest1"); dynamic s = "123"; dynamic str = ooo.main(); Console.WriteLine(str); str = ooo.test1(s); Console.WriteLine(str); }
Python 基础总篇(下)选择结构开始!
目录
- 5. 选择结构
- 5.1 if判断语句:
- 5.1.2 案例
- 5.1.2.1 计算三角形案例
- 5.1.2.2 打车费用计算
- 5.1.2.3 经典案例:计算水仙花
- 5.1.3 if循环嵌套
- 5.1.3.1 献血案例
- 6. 循环结构
- 6.1 循环的套路
- 6.1.1 循环与不循环的比较
- 6.2 while 语法
- 6.2.1 案例所有水仙花(100-999):
- 6.2.2 其他案例
- 6.3 for循环
- 6.3.1 基础for循环
- 6.3.2 其他for循环案例
- 后记
- 列表地址
- 元组 字典 地址
5. 选择结构
5.1 if判断语句:
语法:if not(取反)判断条件:
输出内容
elif 判断条件:
输出内容
else
输出内容
5.1.2 案例
5.1.2.1 计算三角形案例
计算三角形
##计算三角形面积
# 已知三边海伦公式得出三角形面积 改进三角形,三边不成立就停止输出
##需要平方根所以引入数学公式
import math
# 输入三边
a = float(input('请输入a:'))
b = float(input('请输入b:'))
c = float(input('请输入c:'))
# 先求p,p = (a+b+c) / 2
res = a + b < c and a + c < b and b + c < a
if res :
print('请输检查三边输入是否正确')
exit(1)# 1 是错误退出
p = (a + b + c) / 2
# 求s面积
s = math.sqrt(p * (p - a) * (p - b) * (p - c))
##输入面积
print("面积为:%.2f" % s)
5.1.2.2 打车费用计算
# 打车费用计算,输入公里数得到费用
# 1.小于2km 起步 8元
# 2.2 - 10 公里超过起步价每公里2.8
# 3. > 10 公里,每公里 3.5
# 输入公里数
gl = float(input('请输入公里:'))
# 判断输入公里数是否正确(最后)
# 判断以上条件
# 小于2公里的
if gl<2 and gl >=0:
gl = 8
print("%.2f 元" %gl)
elif gl >=2 and gl <= 10:
gl = gl - 2
gl = gl * 2.8 +8
print("%.2f 元" %gl)
elif gl > 10:
gl = (2.8*8+8) + (gl - 10) * 3.5
print("%.2f 元" % gl)
else:
print('请输入正确的公里数')
5.1.2.3 经典案例:计算水仙花
# 判断是否水仙花
# 输入三位数
sxh = int(input('请输入三位数:'))
# 判断输入的是否是三位数
if sxh < 100 or sxh >999:
print('输入内容错误')
exit('输三位数字!!!')
# 输入正确的内容
else:
#转成字符串提取内容
sxh_str = str(sxh)
a = int (sxh_str[0])
b = int (sxh_str[1])
c = int (sxh_str[2])
# 判断是否为水仙花
if a**3 + b**3 + c**3 != sxh:
print('不是水仙花数')
else:
print('是水仙花数')
5.1.3 if循环嵌套
5.1.3.1 献血案例
# 输入性别和体重判断输血量
sex = input('请输入性别:')
weigth = int(input('请输入体重'))
cc = 0
if sex = '1': # 1代表男
if weigth >= 65:
cc = 400
else:
cc = 300
else: # 反之就是女的
if weigth > 45
cc = 300
else:
cc = 200
print ('献血量:%d CC' %cc)
6. 循环结构
6.1 循环的套路
1.考虑好循环的初始条件
2.考虑好结束条件
3.重复需要干嘛
4.如何进入下次循环
6.1.1 循环与不循环的比较
原始方式想要算平均分:
# 原始方式
a = int(input('请输入第1个成绩'))
b = int(input('请输入第2个成绩'))
c = int(input('请输入第3个成绩'))
d = int(input('请输入第4个成绩'))
e = int(input('请输入第5个成绩'))
sum = a+b+c+d+e
avg = sum / 5
print('平均分: %.2f' %avg )
用循环后:
i = 1
sum = 0
while i <= 5 :
a = input('请输入第%d个成绩:'%i)
sum += int(a)
i+=1
avg = sum / 5
print(avg)
6.2 while 语法
循环的内容
注意:
如果循环的条件成立,运行“循环内容”
如果不成立,跳过循环
6.2.1 案例所有水仙花(100-999):
# 找出三位数的所有水仙花数
# 首先要限定范围
n = 100
while (n<=999):
# 拿出个位十位百位相加
sxh_str = str(n)
a = int (sxh_str[0])
b = int (sxh_str[1])
c = int (sxh_str[2])
sum = a**3 + b**3 + c**3
#符合条件干什么
if sum == n :
print('%d是水仙花数' %n)
# 下次进入循环n的值
n += 1
6.2.2 其他案例
https://blog.csdn.net/C2_tr_Grow_up/article/details/116165743
6.3 for循环
6.3.1 基础for循环
# for循环
str = 'Hello World'
for a in str:
print(a)
for c in range(1,100):
print(c)
6.3.2 其他for循环案例
https://blog.csdn.net/C2_tr_Grow_up/article/details/116203513
后记
其他后续内容请查看我发的其他博客,我会一直持续更新。
列表地址
https://blog.csdn.net/C2_tr_Grow_up/article/details/116403483
元组 字典 地址
https://blog.csdn.net/C2_tr_Grow_up/article/details/116429147
Python 多进程(下)和多线程
一、多进程
1. 进程池
由于 Python 中线程封锁机制,导致 Python 中的多线程并不是正真意义上的多线程。当我们有并行处理需求的时候,可以采用多进程迂回地解决。
如果要在主进程中启动大量的子进程,可以用进程池的方式批量创建子进程。
首先,创建一个进程池子,然后使用 apply_async () 方法将子进程加入到进程池中。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2018/5/23 14:15
# @Author : zhouyuyao
# @File : demon1.py
import multiprocessing
import os
import time
from datetime import datetime
def subprocess(number):
# 子进程
print(''这是第{0}个子进程''.format(number))
pid = os.getpid() # 得到当前进程号
print(''当前进程号:{0},开始时间:{1}''.format(pid, datetime.now().isoformat()))
time.sleep(30) # 当前进程休眠30秒
print(''当前进程号:{0},结束时间:{1}''.format(pid, datetime.now().isoformat()))
def mainprocess():
# 主进程
print(''这是主进程,进程编号:{0}''.format(os.getpid()))
t_start = datetime.now()
pool = multiprocessing.Pool()
for i in range(8):
pool.apply_async(subprocess, args=(i,))
''''''pool.apply_async 非阻塞,定义的进程池最大数的同时执行
pool.apply 一个进程结束,释放回进程池,开始下一个进程
pool.apply_async()维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去''''''
pool.close()
''''''Prevents any more tasks from being submitted to the pool.
Once all the tasks have been completed the worker processes will exit.''''''
pool.join()
''''''Wait for the worker processes to exit.
One must call close() or terminate() before using join().''''''
t_end = datetime.now()
print(''主进程用时:{0}毫秒''.format((t_end - t_start).microseconds))
if __name__ == ''__main__'':
# 主测试函数,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行
mainprocess()
我们创建了 7 个子进程,它们的运行状况如下
这是主进程,进程编号:10616
这是第0个子进程
当前进程号:17464,开始时间:2018-05-23T21:22:25.986975
这是第1个子进程
当前进程号:11348,开始时间:2018-05-23T21:22:25.990986
这是第2个子进程
当前进程号:4732,开始时间:2018-05-23T21:22:25.997503
这是第3个子进程
当前进程号:12196,开始时间:2018-05-23T21:22:26.002516
当前进程号:17464,结束时间:2018-05-23T21:22:55.987520
这是第4个子进程
当前进程号:17464,开始时间:2018-05-23T21:22:55.987520
当前进程号:11348,结束时间:2018-05-23T21:22:55.991028
这是第5个子进程
当前进程号:11348,开始时间:2018-05-23T21:22:55.991028
当前进程号:4732,结束时间:2018-05-23T21:22:55.998090
这是第6个子进程
当前进程号:4732,开始时间:2018-05-23T21:22:55.998090
当前进程号:12196,结束时间:2018-05-23T21:22:56.002560
这是第7个子进程
当前进程号:12196,开始时间:2018-05-23T21:22:56.002560
当前进程号:17464,结束时间:2018-05-23T21:23:25.988243
当前进程号:11348,结束时间:2018-05-23T21:23:25.991248
当前进程号:4732,结束时间:2018-05-23T21:23:25.998248
当前进程号:12196,结束时间:2018-05-23T21:23:26.003260
主进程用时:925895毫秒
二、多线程
多线程类似于同时执行多个不同程序,多线程运行有如下优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
- 程序的运行速度可能加快
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
每个线程都有他自己的一组 CPU 寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的 CPU 寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。
- 线程可以被抢占(中断)。
- 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。
线程可以分为:
- 内核线程:由操作系统内核创建和撤销。
- 用户线程:不需要内核支持而在用户程序中实现的线程。
Python3 线程中常用的两个模块为:
- _thread
- threading (推荐使用)
thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用 "thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。
除了使用方法外,线程模块同样提供了 Thread 类来处理线程,Thread 类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start(): 启动线程活动。
- join([time]): 等待至线程中止。这阻塞调用线程直至线程的 join () 方法被调用中止 - 正常退出或者抛出未处理的异常 - 或者是可选的超时发生。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
Python 中使用线程有两种方式:函数或者用类来包装线程对象。
1. 函数式
启动一个线程就是把一个函数传入并创建 Thread 实例,然后调用 start () 开始执行:
threading.Thread(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
"""This constructor should always be called with keyword arguments. Arguments are:
*group* should be None; reserved for future extension when a ThreadGroup
class is implemented.
*target* is the callable object to be invoked by the run()
method. Defaults to None, meaning nothing is called.
*name* is the thread name. By default, a unique name is constructed of
the form "Thread-N" where N is a small decimal number.
*args* is the argument tuple for the target invocation. Defaults to ().
*kwargs* is a dictionary of keyword arguments for the target
invocation. Defaults to {}.
If a subclass overrides the constructor, it must make sure to invoke
the base class constructor (Thread.__init__()) before doing anything
else to the thread.
创建两个线程
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2018/5/23 21:49
# @Author : zhouyuyao
# @File : demon2.py
import threading
def worker(args):
print("开始子线程 {0}".format(args))
print("结束子线程 {0}".format(args))
if __name__ == ''__main__'':
print("start main")
t1 = threading.Thread(target=worker, args=(1,))
t2 = threading.Thread(target=worker, args=(2,))
t1.start()
t2.start()
print("end main")
'''''' 结果
start main
开始子线程 1
结束子线程 1
开始子线程 2
结束子线程 2
end main
''''''
2、lock
多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
来看看多个线程同时操作一个变量怎么把内容给改乱了:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2018/5/23 22:21
# @Author : zhouyuyao
# @File : demon3.py
import time, threading
# 假定这是你的银行存款:
balance = 0
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
change_it(n)
def main():
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
if __name__ == ''__main__'':
main()
我们定义了一个共享变量 balance,初始值为 0,并且启动两个线程,先存后取,理论上结果应该为 0,但是,由于线程的调度是由操作系统决定的,当 t1、t2 交替执行时,只要循环次数足够多,balance 的结果就不一定是 0 了。
原因是因为高级语言的一条语句在 CPU 执行时是若干条语句,即使一个简单的计算:
balance = balance + n
也分两步:
''''''
1、计算balance + n,存入临时变量中
2、将临时变量的值赋给balance
''''''
运行多次之后我们会发现,会出现不一样的结果。
究其原因,是因为修改 balance 需要多条语句,而执行这几条语句时,线程可能中断,从而导致多个线程把同一个对象的内容改乱了。
两个线程同时一存一取,就可能导致余额不对,你肯定不希望你的银行存款莫名其妙地变成了负数,所以,我们必须确保一个线程在修改 balance 的时候,别的线程一定不能改。
如果我们要确保 balance 计算正确,就要给 change_it () 上一把锁,当某个线程开始执行 change_it () 时,我们说,该线程因为获得了锁,因此其他线程不能同时执行 change_it () ,只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过 threading.Lock () 来实现:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2018/5/23 22:35
# @Author : zhouyuyao
# @File : demon4.py
import threading
balance = 0
lock = threading.Lock()
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
def main():
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
if __name__ == ''__main__'':
main()
当多个线程同时执行 lock.acquire () 时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用 try...finally 来确保锁一定会被释放。
锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。
多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离,同时,又要小心死锁的发生。
Python 解释器由于设计时有 GIL 全局锁,导致了多线程无法利用多核。多线程的并发在 Python 中就是一个美丽的梦。
参考资料
1. https://blog.csdn.net/theonegis/article/details/69230167 Python 多进程之进程池
2. http://www.runoob.com/python3/python3-multithreading.html
3. https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143192823818768cd506abbc94eb5916192364506fa5d000
关于python主机批量管理脚本和下的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于(六)Python基础,字符串(下)、C#调用python脚本(二)python代码打包成库供C#调用、Python 基础总篇(下)选择结构开始!、Python 多进程(下)和多线程等相关知识的信息别忘了在本站进行查找喔。
本文标签: