Python 多线程是多鸡肋

http://blog.csdn.net/temanm/article/details/53389963

摘要:

Python 对并行化支持的名声就不是很好,如果你用过 Python 自带的线程库 thread 和 threading,你应该知道 Python 线程其实并不好用。例如:没有自带的获取线程返回值的方法,需要自己重写自己的threading。

目录:

  1. Python 多线程 基础
  2. Python 多线程 阻塞
  3. Python 多线程 获取返回值
  4. Python 多线程 数据对比测试

正文: 
一. Python 多线程 基础 
1.1 thread 模块

# -*- coding: UTF-8 -*-
import thread
import time

# 为线程定义一个函数
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print "%s: %s" % ( threadName, time.ctime(time.time()) )

# 创建两个线程
try:
   thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print "Error: unable to start thread"

while 1:
   pass
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

执行结果:

Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.2 threading模块

# -*- coding: UTF-8 -*-
import threading

def func(a):
    print "input is :" + a


threads = []
for i in range(10):
    threads.append(threading.Thread(target=func, args=str(i)))

for i in threads:
    i.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

执行结果:

D:\Python\python.exe C:/Users/timen.xu/Desktop/CsdnSpider/Test.py
input is :0
input is :1
input is :2
input is :3
input is :4
input is :5
input is :6
input is :7
input is :8
input is :9

Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

二. Python 多线程 阻塞 
2.1 阻塞的例子

import threading
import time


def foo():
    time.sleep(100)


threads = []
for i in range(5):
    threads.append(threading.Thread(target=foo))

for t in threads:
    t.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

你会发现你的 python 脚本不能使用 ctrl+c 来取消了,那么问题来了,为什么会出现之前说的子线程“阻塞”主线程的问题呢?其实这并不是阻塞,默认情况不加任何参数的话:主线程完成后退出,而子线程并不会退出(daemon 属性为 False),此时主线程已经完成退出,没有线程接收 ctrl+c 的信号,子线程相当于没人托管,所以不能停止子线程的输出。 
2.2 解决方法

import threading
import time


def foo():
    time.sleep(100)


threads = []
for i in range(5):
    threads.append(threading.Thread(target=foo)

for t in threads:
    t.daemon = True
    t.start()

while threading.activeCount() > 0:
    time.sleep(1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

首先配置 daemon 为 True,随后每一秒检查一次活跃的子线程数目,直至所有子线程结束。功能相当于手动实现了一个不会 block 信号的对所有线程调用的 join 方法。

三. Python 多线程 获取返回值 
3.1 简介 
其实这才是 Python 线程使用中最常遇到的问题,起了很多个线程调用了很多函数,然而直接用 threading 库基本没有办法获取返回值。 
3.2 采用队列 
这是目前最主流的获取线程数据的方法。使用 Queue 库创建队列实例,用来储存和传递线程间的数据。Python 的队列是线程安全的,也就是说多个线程同时访问一个队列也不会有冲突。Python 队列有三种 FIFO 先进先出,FILO 先进后出(类似栈),优先级队列(由单独的优先级参数决定顺序)。使用队列可以实现简单 生产者 – 消费者 模型

# -*- coding:utf-8 -*-
import threading
import Queue
import random
import time


class Producer(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.data = queue

    def run(self):
        # 生成随机数字并放入队列
        for i in range(10):
            num = random.randint(1,20)
            print "original num", num
            self.data.put(num)
            time.sleep(1)

# 主线程
def main():
    queue = Queue.Queue()
    producer = Producer(queue)
    producer.daemon = True
    producer.start()

    while threading.activeCount() > 0:
        time.sleep(1)

if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

四. Python 多线程 数据对比测试 
4.1 数据对比需求 
对有鱼股票、新浪财经、富途牛牛中个股的今开、昨收、最高、最低、成交额、成交量、最新价进行数值对比,判断有鱼股票提供的数据的正确性,对比的结果误差不查过1% 
需要对比的股票:hs_code_list.xls

4.2 代码编写 
4.2.1 Python xlrd 获取excel需要对比数据的股票

# -*- coding:utf-8 -*-
import xlrd


# 从excel中读取数据
def read_text(excel_name, excel_table):
    data_excel = []
    xls = xlrd.open_workbook(excel_name)
    table = xls.sheet_by_name(excel_table)
    num_row = table.nrows
    for i in range(num_row):
        if i is not 0:
            stock_market = table.row(i)[1].value
            stock_code = table.row(i)[2].value
            data = (stock_market, stock_code)
            data_excel.append(data)
    return data_excel

if __name__ == '__main__':
    print len(read_text("hs_code_list.xls", "Sheet1"))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4.2.2 Python Request获取有鱼股票、新浪财经、富途牛牛个股数据

# -*- coding:utf-8 -*-
import requests
import time
import json


# 有鱼
def request_yff(market_code, stock_code, graph_tab_index, k_not_refresh, stock_type):
    url = "https://market-qa.ruifusoft.com/app/v1/quote/user/query/stockdetail"
    payload = {
        "marketcode": market_code,
        "stockcode": stock_code,
        "graph_tab_index": graph_tab_index,
        "k_not_refresh": k_not_refresh,
        "stock_type": stock_type
    }
    try:
        r = requests.get(url, payload)
        return r.json()
    except Exception, e:
        return "http_error"

# 新浪
def request_sina(stock_market, stock_code):
    url = "http://hq.sinajs.cn/?list=" + stock_market + stock_code
    try:
        r = requests.get(url)
        return r.text
    except Exception, e:
        return "http_error"


# 获取富途security_id
def request_futu_security_id(stock_code):
    url = "http://www.futunn.com/trade/search?k=" + stock_code
    try:
        r = requests.get(url)
        result = r.json()
        security_id = result.get("data")[0].get("security_id")
        return security_id
    except Exception, e:
        return "http_error"



# 富途
def request_futu(stock_code):
    date = str(time.time()).split(".")
    if request_futu_security_id(stock_code) == "http_error":
        return "http_error"
    else:
        url = "http://www.futunn.com/trade/quote-basic?security_id=" + request_futu_security_id(stock_code) + "&_=" + date[0] + date[1]
        r = requests.get(url)
        return r.json()


if __name__ == '__main__':
    print json.dumps(request_yff("sz", "118850", "0", "0", "010101"), sort_keys=True, indent=2)
    print json.dumps(request_futu("150292"), sort_keys=True, indent=2)
    sina = request_sina("sh", "000001")
    print sina
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

执行结果:

D:\Python\python.exe C:/Users/timen.xu/Desktop/mw-test-feature/AT-Mobile-IOS-V1.6.0-5db9aa896b7e819c36134436b7244ff07f63b998/automation/data-compare/DealRequest.py
{
  "code": 0, 
  "data": {
    "107": "010101", 
    "205": 1, 
    "207": 0, 
    "208": 0, 
    "ask": [
        ......  
    ], 
    "marketcode": "sz", 
    "price": {
      "1": "9.65", 
      "16": "", 
      "6": "+0.02", 
      "7": "+0.21%", 
      "ct": 1
    }, 
    "stockcode": "000001", 
    "subtitle": {
      "content": "\u5348\u95f4\u4f11\u5e02 11-29 11:30:03  ", 
      "ct": 2
    }, 
    "title": "\u5e73\u5b89\u94f6\u884c(000001)", 
    "trade_detail": [
      ......
    ]
  }, 
  "msg": ""
}
{
  "code": "0", 
  "data": {
    "comm_rate": null, 
    "look_over": {
      "view_num": 4773, 
      "watch_num": 190130
    }, 
    "marketStatus": 4, 
    "one_queue": null, 
    "price_range": [
      2949.3, 
      3604.7
    ], 
    "quote": {
      ......
    }, 
    "quoteState": 1, 
    "securityStatic": {
      ......
    }
  }, 
  "message": ""
}
var hq_str_sh000001="上证指数,3269.2339,3276.9996,3291.6566,3293.5930,3263.3973,0,0,156706614,173350038164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2016-11-29,11:33:03,00";


Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

4.2.3 设计单个Test Case

# -*- coding:utf-8 -*-
import DealRequest
import CommonMethod
from decimal import Decimal

# 将数据(eg: 366.3612)显示2位小数
def deal_number(result):
    if result == "http_error":
        return "http_error"
    else:
        return CommonMethod.round_number(result)
        # return str(Decimal(float(result)).quantize(Decimal('0.00')))

# 处理 成交量(eg: 2.10亿手 转换成 210000000)
def deal_deal_number(result):
    if result == "http_error":
        return "http_error"
    else:
        if result == 0 or result == "0":
            return str(result)
        else:
            if isinstance(result, float):
                return str(result)
            elif isinstance(result, int):
                return str(result)
            else:
                message = result[-2]
                if message == u"亿":
                    return str(int(float(result[0: -2]) * 100000000))
                elif message == u"万":
                    return str(int(float(result[0: -2]) * 10000))
                else:
                    return str(result[0: -1])


# 处理 成交额(eg: 2.10亿 转换成 210000000)
def deal_deal_value(result):
    if result == "http_error":
        return "http_error"
    else:
        if result == 0 or result == "0":
            return str(result)
        else:
            if isinstance(result, float):
                return str(result)
            elif isinstance(result, int):
                return str(result)
            else:
                message = result[-1]
                if message == u"亿":
                    return str(float(result[0: -1]) * 100000000)
                elif message == u"万":
                    return str(float(result[0: -1]) * 10000)
                else:
                    return str(result)


# 选择股票的区号类型
def stock_code_type(stock_market):
    if stock_market == "sz":
        return "010101"
    if stock_market == "hk":
        return "010104"
    if stock_market == "sh":
        return "010101"
    if stock_market == "us":
        return "010105"


# 对比数据: 今开、昨收、最高、最低、最新价
def compare(stock_market, stock_code, module, yff, sina, futu, yff_original, sina_original, futu_original):
    if yff == "None":
        return CommonMethod.time_now() + u" 【有鱼】" + stock_market + stock_code + " " + module + u" 未找到该个股数据"
    else:
        if sina == "http_error" or yff == "http_error":
            compare_result_sina = "http_error"
        else:
            compare_result_sina = CommonMethod.percentage(float(yff), float(sina))

        if futu == "http_error" or yff == "http_error":
            compare_result_futu = "http_error"
        else:
            compare_result_futu = CommonMethod.percentage(float(yff), float(futu))

        if compare_result_futu == "http_error":
            if compare_result_sina == "fail":
                return "\n" + CommonMethod.time_now() + u" 【有鱼】" + stock_market + stock_code + " " + module + u"数据不一致,差额超过1%。" \
                                                 u"有鱼" + module + u"为: " + yff_original +u"," \
                                                 u"新浪" + module + u"为: " + sina_original + u"," \
                                                 u"富途" + module + u"为:  未找到该个股数据"
            else:
                return "pass"
        elif compare_result_sina == "http_error":
            if compare_result_futu == "fail":
                 return "\n" + CommonMethod.time_now() + u" 【有鱼】" + stock_market + stock_code + " " + module + u"数据不一致,差额超过1%。" \
                                                 u"有鱼" + module + u"为: " + yff_original + u"," \
                                                 u"新浪" + module + u"为: 未找到该个股数据, "  \
                                                 u"富途" + module + u"为: " + futu_original
            else:
                 return "pass"
        else:
            if compare_result_sina == "fail" or compare_result_futu == "fail":
                return "\n" + CommonMethod.time_now() + u" 【有鱼】" + stock_market + stock_code + " " + module + u"数据不一致,差额超过1%。" \
                                                     u"有鱼" + module + u"为: " + yff_original + u"," \
                                                     u"新浪" + module + u"为: " + sina_original + u"," \
                                                     u"富途" + module + u"为: " + futu_original
            else:
                return "pass"


# 数据对比
def compare_data(stock_market, stock_code):
    CommonMethod.debug("stock_market: " + stock_market, " stock_code: " + stock_code)
    yff = DealRequest.request_yff(stock_market, stock_code, "0", "0", stock_code_type(stock_market))
    yff_result = yff.get("data").get("datagrid")
    if yff_result is not None:
        yff_today_price = yff_result[0].get("v")
        yff_yesterday_price = yff_result[1].get("v")
        yff_top_price = yff_result[4].get("v")
        yff_low_price = yff_result[5].get("v")
        yff_deal_number = yff_result[2].get("v")
        yff_deal_value = yff_result[15].get("v")
        yff_values = yff_result[16].get("v")
        yff_price_ratio = yff_result[6].get("v")
        yff_new_price = yff.get("data").get("price").get("1")
    else:
        yff_today_price = "None"
        yff_yesterday_price = "None"
        yff_top_price = "None"
        yff_low_price = "None"
        yff_deal_number = "None"
        yff_deal_value = "None"
        yff_values = "None"
        yff_price_ratio = "None"
        yff_new_price = "None"
    CommonMethod.debug(u"------------- you yu ------------- ", "")
    CommonMethod.debug(u"今开:", yff_today_price)
    CommonMethod.debug(u"昨收:", yff_yesterday_price)
    CommonMethod.debug(u"最高:", yff_top_price)
    CommonMethod.debug(u"最低:", yff_low_price)
    CommonMethod.debug(u"成交量:", yff_deal_number)
    CommonMethod.debug(u"成交额:", yff_deal_value)
    CommonMethod.debug(u"市值:", yff_values)
    CommonMethod.debug(u"市盈率:", yff_price_ratio)
    CommonMethod.debug(u"最新价:", yff_new_price)

    sina = DealRequest.request_sina(stock_market, stock_code)
    sina_result = CommonMethod.sina_result_response(sina)
    # print sina
    if len(sina_result) == 1:
        sina_today_price = "http_error"
        sina_yesterday_price = "http_error"
        sina_top_price = "http_error"
        sina_low_price = "http_error"
        sina_deal_number = "http_error"
        sina_deal_value = "http_error"
        sina_values = "http_error"
        sina_price_ratio = "http_error"
        sina_new_price = "http_error"
    else:
        sina_today_price = sina_result[1]
        sina_yesterday_price = sina_result[2]
        sina_top_price = sina_result[4]
        sina_low_price = sina_result[5]
        sina_deal_number = sina_result[8]
        sina_deal_value = sina_result[9]
        sina_values = ""
        sina_price_ratio = ""
        sina_new_price = sina_result[3]
    CommonMethod.debug(u"------------- sina --------------", "")
    CommonMethod.debug(u"今开:", sina_today_price)
    CommonMethod.debug(u"昨收:", sina_yesterday_price)
    CommonMethod.debug(u"最高:",sina_top_price)
    CommonMethod.debug(u"最低:", sina_low_price)
    CommonMethod.debug(u"成交量:", sina_deal_number)
    CommonMethod.debug(u"成交额:", sina_deal_value)
    CommonMethod.debug(u"市值:", sina_values)
    CommonMethod.debug(u"市盈率:", sina_price_ratio)
    CommonMethod.debug(u"最新价:", sina_new_price)

    futu = DealRequest.request_futu(stock_code)
    if futu == "http_error":
        futu_today_price = "http_error"
        futu_yesterday_price = "http_error"
        futu_top_price = "http_error"
        futu_low_price = "http_error"
        futu_deal_number = "http_error"
        futu_deal_value = "http_error"
        futu_values = "http_error"
        futu_price_ratio = "http_error"
        futu_new_price = "http_error"
    else:
        futu_today_price = futu.get("data").get("quote").get("open_price")
        futu_yesterday_price = futu.get("data").get("quote").get("last_price")
        futu_top_price = futu.get("data").get("quote").get("highest_price")
        futu_low_price = futu.get("data").get("quote").get("lowest_price")
        futu_deal_number = futu.get("data").get("quote").get("volume")
        futu_deal_value = futu.get("data").get("quote").get("turnover")
        futu_values = ""
        futu_price_ratio = ""
        futu_new_price = futu.get("data").get("quote").get("price")
        CommonMethod.debug(u"------------- futu ------------- ", "")
        CommonMethod.debug(u"今开:", futu_today_price)
        CommonMethod.debug(u"昨收:", futu_yesterday_price)
        CommonMethod.debug(u"最高:", futu_top_price)
        CommonMethod.debug(u"最低:", futu_low_price)
        CommonMethod.debug(u"成交量:", futu_deal_number)
        CommonMethod.debug(u"成交额:", futu_deal_value)
        CommonMethod.debug(u"市值:", futu_values)
        CommonMethod.debug(u"市盈率:", futu_price_ratio)
        CommonMethod.debug(u"最新价:", futu_new_price)

    result_today_price = compare(stock_market,
                                 stock_code,
                                 u"今开",
                                 yff_today_price,
                                 deal_number(sina_today_price),
                                 deal_number(futu_today_price),
                                 yff_today_price,
                                 sina_today_price,
                                 futu_today_price)
    result_yesterday_price = compare(stock_market,
                                     stock_code,
                                     u"昨收",
                                     yff_yesterday_price,
                                     deal_number(sina_yesterday_price),
                                     deal_number(futu_yesterday_price),
                                     yff_yesterday_price,
                                     sina_yesterday_price,
                                     futu_yesterday_price)
    result_top_price = compare(stock_market,
                               stock_code,
                               u"最高",
                               yff_top_price,
                               deal_number(sina_top_price),
                               deal_number(futu_top_price),
                               yff_top_price,
                               sina_top_price,
                               futu_top_price)
    result_low_price = compare(stock_market,
                               stock_code,
                               u"最低",
                               yff_low_price,
                               deal_number(sina_low_price),
                               deal_number(futu_low_price),
                               yff_low_price,
                               sina_low_price,
                               futu_low_price)
    result_deal_number = compare(stock_market,
                                 stock_code,
                                 u"成交量",
                                 deal_deal_number(yff_deal_number),
                                 sina_deal_number,
                                 deal_deal_number(futu_deal_number),
                                 yff_deal_number,
                                 sina_deal_number,
                                 futu_deal_number)
    result_deal_value = compare(stock_market,
                                stock_code,
                                u"成交额",
                                deal_deal_value(yff_deal_value),
                                sina_deal_value,
                                deal_deal_value(futu_deal_value),
                                yff_deal_value,
                                sina_deal_value,
                                futu_deal_value)
    result_new_price = compare(stock_market,
                               stock_code,
                               u"最新价",
                               yff_new_price,
                               deal_number(sina_new_price),
                               deal_number(futu_new_price),
                               yff_new_price,
                               sina_new_price,
                               futu_new_price)
    if result_today_price is "pass" and result_yesterday_price is "pass" and result_top_price is "pass" and \
                    result_low_price is "pass" and result_deal_number is "pass" and \
                    result_deal_value is "pass" and result_new_price is "pass":
        return "all_pass"
    else:
        compare_result = ""
        if result_today_price is not "pass":
            compare_result += result_today_price
        if result_yesterday_price is not "pass":
            compare_result += result_yesterday_price
        if result_top_price is not "pass":
            compare_result += result_top_price
        if result_low_price is not "pass":
            compare_result += result_low_price
        if result_deal_number is not "pass":
            compare_result += result_deal_number
        if result_deal_value is not "pass":
            compare_result += result_deal_value
        if result_new_price is not "pass":
            compare_result += result_new_price
        return compare_result

if __name__ == '__main__':
    print compare_data("sh", "000001")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299

4.2.4 创建测试集

# -*- coding:utf-8 -*-
import HTMLTestRunner
import unittest
import xmlrunner
import CommonMethod
import CommonVariable
import Mulithread


# 测试类
class MyTestSuite(unittest.TestCase):
    def __init__(self, methodName='runTest', param=None):
        super(MyTestSuite, self).__init__(methodName)
        self.param = param

    @staticmethod
    def parametrize(testcase_klass, param=None):
        testloader = unittest.TestLoader()
        testnames = testloader.getTestCaseNames(testcase_klass)
        suite = unittest.TestSuite()
        for name in testnames:
            suite.addTest(testcase_klass(name, param=param))
        return suite

# 单个测试case
class TestOne(MyTestSuite):
    def test_market_compare_data(self):
        print 'Stock =', self.param
        for i in range(CommonVariable.threading_number):
            print "Test Stock: " + CommonVariable.result_excel[self.param + i][0] + CommonVariable.result_excel[self.param + i][1]
        self.assertEqual(Mulithread.test_data(self.param),
                         "", Mulithread.test_data(self.param).encode("utf-8"))

# 测试集
def TestSuite():
    test_suite = unittest.TestSuite()
    for i in range(0, len(CommonVariable.result_excel), CommonVariable.threading_number):
        test_suite.addTest(MyTestSuite.parametrize(TestOne, param=i))
    return test_suite


if __name__ == '__main__':
    choose = "other"
    if choose == "html":
        # 生成html测试报告
        filePath = CommonMethod.current_path() + "/TestReport.html"
        fp = open(filePath, "wb")
        runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title="Python Test Report",
                                               description="This is Python Report")
        runner.run(TestSuite())
        fp.close()
    elif choose == "xml":
        # 生成xml测试报告
        testRunner=xmlrunner.XMLTestRunner(output='test-reports')
        testRunner.run(TestSuite())
    else:
        # 调试测试结果
        testSuite = unittest.TestSuite(TestSuite())
        unittest.TextTestRunner(verbosity=2).run(testSuite)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

4.3 多线程运用 
4.3.1 简介 
由于需要数据对比的数据过多,总共需要对比12444支股票的数据,这个时候考虑用多线程,这边初始化多线程数:

# 线程数
threading_number = 10
  • 1
  • 2

对每个线程返回的测试结果,用队列(QUEUE)将测试结果存储,提供给主线程,得到单个Test Case的测试结果,代码如下:

# -*- coding:utf-8 -*-
import threading
import TestCase
import CommonVariable
import Queue
import DealExcel

queue = Queue.Queue()

class Producer(threading.Thread):
    def __init__(self, q, excel_index):
        threading.Thread.__init__(self)
        self.data = q
        self.excel_index = excel_index

    def run(self):
        test_result = []
        for i in range(CommonVariable.threading_number):
            if i < len(DealExcel.read_text("hs_code_list.xls", "Sheet1")):
                test_result.append(TestCase.compare_data(CommonVariable.result_excel[self.excel_index + i][0], CommonVariable.result_excel[self.excel_index + i][1]))
        self.data.put(test_result)

def test_data(excel_index):
    p = Producer(queue, excel_index)
    p.setDaemon(True)
    p.start()
    test_result = []
    while queue.empty():
        pass
    else:
        test_result.append(queue.get())
    test_result_data = ""
    for i in test_result:
        for m in i:
            if m is not "all_pass":
                test_result_data += m
    return test_result_data

if __name__ == '__main__':
    print test_data(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

4.3.2 公共方法 
四舍五入本来用Python自带的round和decimal,哎,这两个方法并没有做到真正的四舍五入

# 四舍五入
def round_number(obj):
    left = str(obj).split(".")[0]
    right = str(obj).split(".")[1]
    if len(right) == 2:
        return str(float(left + "." + right[0: 2]))
    else:
        if int(right[2]) >= 5:
            return str(float(left + "." + right[0: 2]) + 0.01)
        else:
            return str(float(left + "." + right[0: 2]))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

判断两个得到的返回值(float)进行容错处理,不超过1%

# 判断两个数字相差不超过1%
def percentage(compare_a, compare_b):
    compare_a = float(compare_a)
    compare_b = float(compare_b)
    if compare_a == compare_b or compare_a == 0.00 or compare_b == 0.00:
        return "pass"
    elif compare_a > compare_b:
        if ((compare_a - compare_b) / compare_b) <= 0.01:
            return "pass"
        else:
            return "fail"
    else:
        if ((compare_b - compare_a) / compare_a) <= 0.01:
            return "pass"
        else:
            return "fail"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4.4 代码分享:百度云盘

结论: 
Python 多线程 数据对比测试 
单线程执行 
需要的时间:7803.08秒 
这里写图片描述

多线程执行 
需要的时间:6031.11秒

多线程并没有大大的缩短执行的时间,缩短的效果不是很明显,在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。

参考文献: 
Python 线程浅谈:阻塞和获取返回值 
Python 多线程 
为什么有人说 Python 的多线程是鸡肋呢? 
为什么在Python里推荐使用多进程而不是多线程?

posted on 2017-09-10 22:59  小西红柿  阅读(841)  评论(0)    收藏  举报

导航