app自动化11 线程实现多个脚本在多个手机运行

线程

多任务简单介绍

- 有很多事情在现实生活的场景中是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的。
- 多任务,就是能够在同一时间同时进行多个任务。
这样同时进行多个任务,有一个极大的好处,那就是节省时间
代码举例
import time
import threading

def sing():
    for i in range(5):
        print("唱歌")
        time.sleep(1)

def dance():
    for i in range(5):
        print("跳舞")
        time.sleep(1)

t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)

t1.start()
t2.start()
import time
import threading

def sing():
    for i in range(5):
        print("唱歌")
        time.sleep(1)

def dance():
    for i in range(5):
        print("跳舞")
        time.sleep(1)

sing()
dance()
第一段代码开启3个线程,分别是主线程,子线程t1,子线程t2,耗时一共是5秒
第二段代码开启1个线程,那就是主线程,耗时一共是10秒

为什么第一段代码耗时只要5秒,而第二段代码耗时要10秒呢?

下面就由sunt来为大家说下原因吧,先分析第一段代码:
主线程先开始运行,然后让两个小弟,也就是子线程t1,t2。分别去执行sing()和dance()代码,这两个小弟是并行执行的,并不是说小弟t1先执行完,
小弟t2才去执行,而是两个小弟同时执行,故耗时为5秒,图解如下:
打印出来结果:
唱歌
跳舞
跳舞
唱歌
跳舞
唱歌
跳舞
唱歌
跳舞
唱歌
这是因为当他们从休眠到唤醒是同时的,故资源掠夺的不确定性,有可能跳舞先执行,也有可能唱歌先执行,不过一定是成对出现的。
并行执行耗时总时间为5秒。

接下来分析第二段代码:
主线程先开始执行,然后主线程执行sing()代码块,等待sing()代码块执行完之后,才开始执行dance()代码块。故耗时为sing()代码块
的时间加dance()代码块的时间,一共为10秒。图解如下:

多任务的原理

什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。
单核cpu工作原理
- 现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?
- 答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。实际上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。
- 真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
多核cpu工作原理
和单核类似,相当于多了一个干活的人。
并发:指的是任务数多于cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行
(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的
- 并行和并发都算是多任务,但并行实际上才是真正的多任务,并发是假的。

线程的两张创建方式

- 直接使用threading模块的Thread类,指定要执行的方法,再调用start
- 使用继承的方式,继承Thread类,重新run方法,创建这个对象后,再调用start

查看当前程序线程数量

threading.enumerate()
- 获取所有线程,返回的是一个列表。
- 如果需要个数,使用len(threading.enumerate())

为子线程传递参数

target方式
import time
import threading 

def sing(nums):
    for i in range(nums):
        print("唱歌")
        time.sleep(1)

def dance():
    for i in range(5):
        print("跳舞")
        time.sleep(1)

t1 = threading.Thread(target=sing, args=(3,))
t2 = threading.Thread(target=dance)

t1.start()
t2.start()
类继承方式
import threading

class Work1(threading.Thread):

    def __init__(self, nums):
        super().__init__()
        self.nums = nums

    def run(self):
        for i in range(self.nums):
            print("haha")


def main():
    w = Work1(2)
    w.start()

if __name__ == "__main__":
    main()

Appium多端口

目标,让一个脚本去跑到多台手机。
注意点: appium sever端口要不同,开启多个。bootstrap端口要不同,开启多个。udid需要指定,udid表示设备的唯一表示符号,通过 adb devices 查看。 前半部分都是,比如模拟器的(192.168.57.101:5555)。
appium server  和  bootstrap 和 udid 应该是成对出现的。
命令:
appium -p 4723 -bp 4724 -U 192.168.57.101:5555
-p 表示 appium的端口
-bp 表示 bootstrap的端口
-U 表示设备的标识符
修改init_driver让,init_driver接受port的参数。并且进行对应的连接。
记得,创建的是不同的driver对象。
因为如果使用threading.Thread的这种形式,需要指定执行的函数,所以,把需要执行的代码,封装成一个函数。然后使用
    ports = ["4723", "4725"]

    for i in ports:
        threading.Thread(target=do, args=(i,)).start()
来去执行创建多个driver并且进行脚本的操作。
下面看图解就知道appium多端口的工作原理了。
依照上图可以知道,如果要想让一段脚本操作两个手机,那么我要做的就是,开启两个appium服务和两个bootstrap服务,并且端口号不能相同,
appium和bootstrap是一对,并且有两个手机,还要指定手机设备号,故命令如下
appium -p 4723 -bp 4724 -U 192.168.57.101:5555
    
appium -p 4725 -bp 4726 -U 192.168.57.102:5555

代码实现

命令行先启动appium并指定端口,并指定bootstrap端口和手机设备号
appium -p 4723 -bp 4724 -U 192.168.57.101:5555
    
appium -p 4725 -bp 4726 -U 192.168.57.102:5555
base_driver.py
from appium import webdriver


def init_driver(port="4723"):
    # server 启动参数
    desired_caps = dict()
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.164.101:5555'
    # app信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'
    # 中文
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True
    # 不重置应用
    desired_caps['noReset'] = True
    # toast
    # desired_caps['automationName'] = 'Uiautomator2'
    # 声明对象
    driver = webdriver.Remote('http://localhost:' + port + '/wd/hub', desired_caps)
    return driver
login_page.py
from base_action import BaseAction


class LoginPage(BaseAction):

    pass
demo.py
import threading

from selenium.webdriver.common.by import By

from base_driver import init_driver
from base_action import BaseAction
from login_page import LoginPage


def do(port):
    driver = init_driver(port)
    login_page = LoginPage(driver)
    if "4723" == port:
        login_page.click((By.XPATH, "text,更多"))
    else:
        login_page.click((By.XPATH, "text,WLA"))

def main():
    //根据ports不同,driver可以连接不同的手机
    ports = ["4723", "4725"]

    for i in ports:
        threading.Thread(target=do, args=(i,)).start()

if __name__ == '__main__':
    main()
posted @ 2020-10-13 13:11  幸福来之不易  阅读(747)  评论(0编辑  收藏  举报