RF本身没有多线程的库,我们可以使用类似下面的方法,并对外公开parseArg和multipleArg两个方法(写到测试类里面),给需要并发的函数套上装饰器,即可实现多线程执行函数。由于可以只传单个的参数,因此并不影响函数的单线程运行。
比如在RIDE/RED内可以写成这样的调用
多线程关键字是baidusearch
${arg1} | parseArg | mi | |
${arg2} | parseArg | 故宫喵 | |
${res} | baidusearch | ${arg1} | ${arg2} |
log many | ${res} |
或是这样调用(多线程关键字是printer),需要导入一个内建库
** Settings **
Library Collections # 这里一定要注意大小写拼写
${li} | Create List | ||
:For | ${i} | in range | ${4} |
\ | ${arg} | parseArg | 循环线程${i} |
\ | append to list | ${li} | ${arg} |
${res} | printer | @{li} | |
log many | ${res} |
需要注意的是,最后printer的时候需要@{li}而非${li}。@{li}本质上是解包,也就是相当于*args,把列表拆分成多个参数传入,而$会把列表当成一个整体传入
而multipleArg则要这样使用(关键字还是printer)
${arg} | parseArg | 统一编号 | |
${li} | multipleArg | ${arg} | ${4} |
${res} | printer | @{li} | |
log many | ${res} |
这里一定要记得multipleArg后面要传数值格式的参数
多线程装饰器的代码实现如下
# coding: utf-8
from threading import Thread
# resset=[] # 储存线程函数返回值的列表,这一句其实可以不要,因为在multiThread开启的时候会自动在外部创建全局变量resset为[](假如resset不存在,否则即赋值)
def getResult(func): # 重定向函数返回的结果
def infunc(*args,**kwargs):
args=list(args)
tid=args.pop() # 去掉最后一个代表线程编号的参数
args=tuple(args)
try:
resset.append((func(*args,**kwargs),tid)) # 结果是一个由二元元组组成的列表,每个线程一个元组,元组内第一个元素是线程运行的结果,第二个是线程编号
except Exception,e:
resset.append((e,tid)) # 如果线程内报错,就返回错误对象
return infunc
def multiThread(func): # 实现多线程
def infunc(*args):
global resset
resset=[] # 每次调用前,清除结果集
tli=[] # 线程对象列表
tid=0 # 初始线程编号
for arg in args: # 处理传入的参数列表
if len(arg)==1:
if type(arg[0])==type({}):
targ=() # 设置位置参数
darg=arg[0] # 设置命名参数
else:
targ=arg[0]
darg={}
elif len(arg)==0 or arg==None:
targ=()
darg={}
else:
targ=arg[0] # (args,kwargs) [0]
darg=arg[1] # (args,kwargs) [1]
try:
assert type(targ)==type(()) and type(darg)==type({}) # 确定传来的参数格式正确
except:
raise ValueError, 'arguement format error'
targ=tuple(list(targ)+[tid]) # 在位置参数的最后一个位置,加上线程编号传进去。getResult里会把这个参数pop掉,并记录到结果里
tli.append(Thread(target=func,args=targ,kwargs=darg)) # 线程列表填充
tid=tid+1 # 线程编号递增
for th in tli:
th.setDaemon(True)
th.start()
for th in tli:
th.join() # 大家都开始了才join阻塞
return {x[1]:x[0] for x in resset} # 结果字典,resset的结果是二元元组,0号元素是函数返回值,1号元素是线程编号tid
return infunc
def parseArg(*args,**kwargs): # 打包单个线程的参数
return (args,kwargs)
def multipleArg(gtuple,times): # 复制某个线程的参数n次,形成参数元组
return (gtuple,)*times
####################################### 分割线,下为演示部分 #######################################
from selenium import webdriver
import time, re
@multiThread
@getResult
def baidusearch(statement):
driver=webdriver.Chrome()
driver.get(r'http://www.baidu.com')
driver.maximize_window()
driver.implicitly_wait(10)
kw=driver.find_element_by_id('kw')
kw.clear()
kw.send_keys(statement)
driver.find_element_by_id('su').click()
time.sleep(2)
resNumber=driver.find_element_by_xpath("//span[@class='nums_text']").text
resNumber=re.findall('[0-9,]+',resNumber)[0]
driver.close()
return resNumber
li1=parseArg('mi')
li2=parseArg('wo')
li3=parseArg(u'故宫喵')
print(baidusearch(li1,li2,li3))
lis=multipleArg(parseArg('python'),3) # 或者可以像下面这样,连续生成多个同样的关键字
print(baidusearch(*lis)) # 这种方式不要忘记解包
lit=tuple([ parseArg('html'+str(x)) for x in range(1,6) ]) # 使用推导式,分别搜索html1 html2 ... html5
print(baidusearch(*lit))
################################# 可以使用路由的方式,让不同的函数并发执行 #######################
def plus(x,y):
return x+y
def minus(x,y):
return abs(x-y)
@multiThread
@getResult
def union(func_name,*args,**kwargs): # 可以用这种方法来实现路由,让不同的函数同时执行在一个线程里
return eval(func_name)(*args,**kwargs)
li1=parseArg('plus',1,2)
li2=parseArg('minus',3,y=5)
li3=parseArg('plus',x=4,y=6)
print(union(li1,li2,li3))
后记:这个方法当时并没有发现并不能支持类里的函数,所以有了改进版如下
https://blog.csdn.net/qq_27884799/article/details/89647075