多线程并发程序设计与分析

by:授客 QQ:1033553122

1.技术难点分析与总结

难点1:线程运行时,运行顺序不固定

难点2:同一段代码,再不加锁的情况下,可能被多个线程同时执行,这会造成很多麻烦,比如变量的赋值不正确,方法的重复调用,而如果加锁,或者通过join阻塞方式等来控制,那么又如同运行单进程,效率低下,达不到,“并发”,“高速”的效果。

难点3:不通过join阻塞等方式,主线程可能会优先于子线程退出,这也会导致问题,比如子线程还在用文件句柄,主线程就把文件关闭了。

解决方法:

1、考虑为线程类添加变量属性,这样一来,每个线程都拥有自己的变量,互不影响,比如下面例子中用到的run_times

2、线程公用的一些变量,也可以考虑通过线程类的变量属性传递,比如下面例子中多线程用到的文件句柄file_handler

3、必要时,关键代码可以考虑枷锁Lock、RLock,具体自己看官方文档,比如下方的文件写入,不同线程可能会在同一行写入数据,导致数据统计时不准确,所以加锁,如果出于速度考虑,可以考虑分别给每个线程传递属于自己的文件句柄,写入不同的文件,

4、清理工作,关于这个,需要知道2点:

1)main线程退出时,不会kill非守护线程,但是会kill守护线程

2)通常,子线程start()后会去调用run方法,运行完run方法,子线程停止执行,不会继续运行之后的代码。


所以,通常我们可以这么做,获取当前活动线程数,如果线程数为1,则说明子线程都运行完,可以继续后面的代码清理工作,否则继续循环检测,这里还可以加代码优化,比如每隔一段时间检测一次,以免主线程浪费系统资源

  

# 利用主线程执行清理工作

    current_active_thread_num
=

len
(threading.enumerate())

# 获取当前活动线程数量

while
 
current_active_thread_num
!= :
#

10秒检测一次
    current_active_thread_num
=
len(threading.enumerate())

2.代码实践


requestpy.py


#!/usr/bin/env python

#
-*- coding:utf-8 -*-


__author__
=
'
shouke'

import

urllib.request
import

json
import

sys
import

threading
from

collections
import

Counter
import

time
import

datetime

class

SubThread(threading.Thread):
    mutex_lock
= threading.RLock()
    def

__init__(self,
file_handler):
        self.file_handler
= file_handler

        self.run_times
=

# 记录每个线程的运行次数

        threading.Thread.__init__(self)

def

run(self):
        while

self.run_times
<</span>
int]):
            url
=
'http://
xxxxxx/xxxxxcard/kq/codepool/test/'

            request
= urllib.request.Request(url,
method='POST')
            try:
                response
= urllib.request.urlopen(request)
                response_body
= response.read()
                response_body
= response_body.decode('utf-8')
                response_body
= json.loads(response_body)

#
写入文件

                SubThread.mutex_lock.acquire()
                self.file_handler.write(str(response_body['code']))
                self.file_handler.write('\n')
                SubThread.mutex_lock.release()

self.run_times
=
self.run_times
+
# 记录每个线程的运行次数

                print('已经执行%s次请求'

%
str(self.run_times))
            except

Exception
as

e:
                print('请求错误%s'

% e)

def

analyze(test_result_data):
    list_data
= []
      #
存放目标数据

    total_line_count
=
0  #
读取的文本行数

    abnormal_line
=
0
   #
存放异常数据行数

    digit_line
=
0
      #
存放正确数据函数


    with
 
open(test_result_data,

'r'
)

as

file:
        line
= file.readline()
        while

line:
            line
= line.strip('\n')
            if

line.isdigit()
and

len(line)
== :
                list_data.append(int(line))
                digit_line
= digit_line +
            else:
                abnormal_line
= abnormal_line +
                print('服务器返回数据异常')

line
= file.readline()
            total_line_count
= total_line_count +

print('读取的总行数:%s'

%
str(total_line_count))
    print('数据正确的行数:%s'

%
str(digit_line))
    print('数据异常的行数:%s'

%
str(abnormal_line))

#
分析是否存在重复数据

    set_data
=
set(list_data)
    if

len(set_data)
==
len(list_data):
        print('不存在重复数据,
总数:%s
条'

%
len(list_data))
    else:
        print('有重复数据,重复数据:%s条'

% (len(list_data)
-
len(set_data)))

if

__name__ ==
'__main__'
:
    start_time
= datetime.datetime.now()

test_result_data =
'd:
\\test_result_data.txt'
    file
=  open(test_result_data,

'w'
)
 #
存储服务器返回数据


    threads_pool
= []  #
线程池,存放线程对象

    thread_num
=
0  #
记录创建的线程数量


    while

thread_num <</span>
int]):
        thread_obj
= SubThread(file)
        threads_pool.append(thread_obj)
        thread_num
= thread_num +

for

thread
in

threads_pool:
        thread.start()

#
利用主线程执行清理工作

    current_active_thread_num
=
len(threading.enumerate())

# 获取当前活动线程数量

    while
 
current_active_thread_num
!= :
)
        current_active_thread_num
=
len(threading.enumerate())

#
清理工作

    try:
        file.close()
    except

Exception
as

e:
        print('关闭文件出错%s'

% e)

end_time = datetime.datetime.now()
    print('运行耗时:',end_time
- start_time)


# 分析数据

    analyze(test_result_data)

运行(禁用time.sleep函数的情况下):

100个线程,每个线程运行50次,总的运行

5000次

python requestpy.py
100
50

修改程序如下

class SubThread(threading.Thread):
    def __init__(self, file_handler):
        self.file_handler = file_handler
        self.run_times = 0 # 记录每个线程的运行次数
        threading.Thread.__init__(self)

def run(self):
        while self.run_times < int(sys.argv[2]):
            url = 'http://xxxxxx/xxxxxcard/kq/codepool/test/'

            request = urllib.request.Request(url, method='POST')
            try:
                response = urllib.request.urlopen(request)
                response_body = response.read()
                response_body = response_body.decode('utf-8')
                response_body = json.loads(response_body)

# 写入文件
                self.file_handler.write(str(response_body['code']))
                self.file_handler.write('\n')

self.run_times = self.run_times + 1 # 记录每个线程的运行次数
                print('已经执行%s次请求' % str(self.run_times))
            except Exception as e:
                print('请求错误%s' % e)

def analyze(test_result_file_list):
    list_data = []       # 存放目标数据
    total_line_count = 0  # 读取的文本行数
    abnormal_line = 0    # 存放异常数据行数
    digit_line = 0       # 存放正确数据函数

    for file in test_result_file_list:
        with  open(file, 'r'as file:
            line = file.readline()
            while line:
                line = line.strip('\n')
                if line.isdigit() and len(line) == 12:
                    list_data.append(int(line))
                    digit_line = digit_line + 1
                else:
                    abnormal_line = abnormal_line + 1
                    print('服务器返回数据异常')

line = file.readline()
                total_line_count = total_line_count + 1

print('读取的总行数:%s' % str(total_line_count))
    print('数据正确的行数:%s' % str(digit_line))
    print('数据异常的行数:%s' % str(abnormal_line))

# 分析是否存在重复数据
    set_data = set(list_data)
    if len(set_data) == len(list_data):
        print('不存在重复数据, 总数:%s 条' % len(list_data))
    else:
        print('有重复数据,重复数据:%s条' % (len(list_data) - len(set_data)))

# 获取重复数据
    filehaneder = open('d:\\repeat_data.txt''w')
    c = Counter(list_data)
    for item in c.items():
        if item[1] > 1:
            print('重复数据:%s' % item[0])
            filehaneder.write(str(item[0]))
            filehaneder.write('\n')
    filehaneder.close()

if __name__ == '__main__':
    start_time = datetime.datetime.now()
    base_filename = 'test_result_data'
    base_dirname = 'd:\\result\\'
    test_result_file_list = [] # 存储结果数据文件名
    sub_thread_file_list = [] # 每个线程的文件句柄

    threads_pool = []  # 线程池,存放线程对象
    thread_num = 0  # 记录创建的线程数量

    while thread_num < int(sys.argv[1]):
        filename = base_dirname + base_filename + str(thread_num + 1) + '.txt'
        test_result_file_list.append(filename)
        file =  open(filename, 'w')
        sub_thread_file_list.append(file)

thread_obj = SubThread(file)
        threads_pool.append(thread_obj)
        thread_num = thread_num + 1

for thread in threads_pool:
        thread.start()

# # 利用主线程执行清理工作
    current_active_thread_num = len(threading.enumerate()) # 获取当前活动线程数量
    while  current_active_thread_num != 1:
        time.sleep(300)
        current_active_thread_num = len(threading.enumerate())

# 清理工作
    try:
        for file in sub_thread_file_list:
            file.close()
    except Exception as e:
        print('关闭文件出错%s' % e)

end_time = datetime.datetime.now()
    print('运行耗时:',end_time - start_time)

# 分析数据
    analyze(test_result_file_list)

运行结果:

Python 多线程并发程序设计与分析的更多相关文章

  1. python 多线程日志切割+日志分析

    python 多线程日志切割+日志分析 05/27. 2014 楼主最近刚刚接触python,还是个小菜鸟,没有学习python之前可以说楼主的shell已经算是可以了,但用shell很多东西实现起来 ...

  2. 用Queue控制python多线程并发数量

    python多线程如果不进行并发数量控制,在启动线程数量多到一定程度后,会造成线程无法启动的错误. 下面介绍用Queue控制多线程并发数量的方法(python3). # -*- coding: utf ...

  3. Python多线程并发的误区

    由于项目要做一个并发测试,由于断言的东西较多,决定手写脚本.于是用python写了脚本: def test_method(thread_no): print("%s===test_metho ...

  4. python 多线程并发threading & 任务队列Queue

    https://docs.python.org/3.7/library/concurrency.htmlpython程序默认是单线程的,也就是说在前一句语句执行完之前后面的语句不能继续执行先感受一下线 ...

  5. python多线程并发

    # coding=utf8 # 使用前需安装net-snmp-utils或net-snmp包 from _utils.patrol2 import run_cmd import sys import ...

  6. python 多线程 并发socket实例

    sever side: import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self ...

  7. Python 多线程教程:并发与并行

    转载于: https://my.oschina.net/leejun2005/blog/398826 在批评Python的讨论中,常常说起Python多线程是多么的难用.还有人对 global int ...

  8. HashMap多线程并发问题分析

    转载: HashMap多线程并发问题分析 并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题. ...

  9. python多进程并发和多线程并发和协程

    为什么需要并发编程? 如果程序中包含I/O操作,程序会有很高的延迟,CPU会处于等待状态,这样会浪费系统资源,浪费时间 1.Python的并发编程分为多进程并发和多线程并发 多进程并发:运行多个独立的 ...

随机推荐

  1. python中os.path.isdir()等函数的作用和用法

    一 用法和概念: Python中的os模块用于和系统进行交互,其中: 1 os.listdir()用于返回一个由文件名和目录名组成的列表,需要注意的是它接收的参数需要是一个绝对的路径. 2 os.pa ...

  2. python的函数学习2

    名称空间 用来存放名字的地方,有三种名称空间:内置名称空间,全局名称空间,局部名称空间. 比如执行test.py: python test.py .python解释器先启动,因而首先加载内置名称空间 ...

  3. 基于 Consul 实现 MagicOnion(GRpc) 服务注册与发现

    0.简介 0.1 什么是 Consul Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置. 这里所谓的服务,不仅仅包括常用的 Api 这些服务,也包括软件开发过程 ...

  4. java提高(3)---正则表达式(2)

    正则表达式 说真的正则表达式真不好写,当我收集资料准备开始写的时候,发现收集的东西越来越多范围也越来越广,我文章的前提就是文章要清晰, 在缕清自己思路之后,我从先简后难的方式来写有关正表达式,你们如果 ...

  5. redis简单应用

    启动和结束 --启动redis服务 E:\redis>redis-server.exe redis.windows.conf --结束redis服务 127.0.0.1:6379> shu ...

  6. Nexus私服搭建

    maven私服的搭建 --> maven -->{ 1,本地仓库(从中央仓库下载保存到本地的或者自己到网上下载的jar文件包) 2,远程仓库 -->{ 1,中央仓库(maven官方j ...

  7. Java多线程之一

    进程与线程 进程 进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位,比如我们windows电脑上运行的一个程序就是一个进程.在传统进程中进程是资源分配和调度的一个基本单位,在后来引入线 ...

  8. python三大神器之fabric(2.0新特性)

    fabric经常出现在自动化运维领域,批量处理一些运维工作.fabric是在paramiko之上又封装了一层,操作起来更加简单易用. 本来只是想写个博客记录一下,然后发现之前写的代码不能运行了,报以下 ...

  9. spring-boot-2.0.3之quartz集成,最佳实践

    前言 开心一刻 快过年了,大街上,爷爷在给孙子示范摔炮怎么放,嘴里还不停念叨:要像这样,用劲甩才能响.示范了一个,两个,三个... 孙子终于忍不住了,抱着爷爷的腿哭起来:爷呀,你给我剩个吧! 新的一年 ...

  10. 使用3D Slicer进行颅骨去除

    关于3D Slicer的下载.安装及模块安装在上一篇博客中以及介绍过,以下将专注于使用3D Slicer进行颅骨去除 准备 此次,我们需要安装SwissSkullStripper模块,安装后需要重启软 ...