热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python多线程编程技巧与实战应用详解

篇首语:本文由编程笔记#小编为大家整理,主要介绍了python多线程创建与使用(转)相关的知识,希望对你有一定的参考价值。 原文:http://codingpy.com/article/python-

篇首语:本文由编程笔记#小编为大家整理,主要介绍了python多线程创建与使用(转)相关的知识,希望对你有一定的参考价值。


原文:http://codingpy.com/article/python-201-a-tutorial-on-threads/


创建多线程

创建多线程主要有2种方式。



  • 使用threading.Thread函数

  • 继承threading类


1. 使用threading.Thread函数

import threading
def tom(number):
print threading.currentThread().getName()
print number
if __name__ == "__main__":
number = ["zero", "one", "two", "three", "four"]
sex = ["man", "woman"]
for i in range(5):
th = threading.Thread(target=tom, args=(number[i],))
# th.setName('mythread')
# print th.getName()
th.start()

说明:Thread()函数有2个参数,一个是target,内容为子线程要执行的函数名称;另一个是args,内容为需要传递的参数。Args 参数看起来有些奇怪,那是因为我们需要传递一个序列给tom函数,但它只接受一个变量,所以我们把逗号放在尾部来创建只有一个参数的序列。创建完子线程,将会返回一个对象,调用对象的start方法,可以启动子线程。

当你运行以上这段代码,会得到以下输出:

Thread-1
zero
Thread-2
one
Thread-3
two
Thread-4
three
Thread-5
four

线程对象的方法:



  • Start() 开始线程的执行


  • Run() 定义线程的功能的函数


  • Join(timeout=None) 程序挂起,直到线程结束;如果给了timeout,则最多阻塞timeout秒


  • getName() 返回线程的名字


  • setName() 设置线程的名字


  • isAlive() 布尔标志,表示这个线程是否还在运行


  • isDaemon() 返回线程的daemon标志


  • setDaemon(daemonic) 把线程的daemon标志设为daemonic(一定要在start()函数前调用)


  • t.setDaemon(True) 把父线程设置为守护线程,当父进程结束时,子进程也结束



2. 继承threading类

import threading
class mythread(threading.Thread):
def __init__(self,number):
threading.Thread.__init__(self)
self.number = number
def run(self):
print threading.current_thread().getName()
print self.number
if __name__ == "__main__":
for i in range(5):
th = mythread(i)
th.start()

当你运行以上这段代码,会得到以下输出:

Thread-1
0
Thread-2
1
Thread-3
2
Thread-4
3
Thread-5
4

当然,通常情况下你不会希望输出打印到标准输出。如果不幸真的这么做了,那么最终的显示效果将会非常混乱。你应该使用 Python 的 logging 模块。它是线程安全的,并且表现出色。让我们用 logging 模块修改上面的例子并且给我们的线程命名。代码如下:

import threading
import logging
def get_logger():
#创建一个被设置为调试级别的日志记录器
logger = logging.getLogger("mylogger")
logger.setLevel(logging.DEBUG)

#设置每行日志的格式。格式包括时间戳、线程名、日志记录级别以及日志信息
fh = logging.FileHandler("threading.log")
fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
fh.setFormatter(formatter)

logger.addHandler(fh)
return logger
def tom(number, logger):
logger.debug(number)
if __name__ == "__main__":
logger = get_logger()
number = ["zero", "one", "two", "three", "four"]
sex = ["man", "woman"]
for i in range(5):
th = threading.Thread(target=tom, args=(number[i],logger))
# th.setName('mythread')
# print th.getName()
th.start()

通过继承的方法:

import threading
import logging
class mythread(threading.Thread):
def __init__(self,number,logger):
threading.Thread.__init__(self)
self.number = number
self.logger = logger
def run(self):
self.logger.debug("calling-thread")
tom(self.number, self.logger)
def get_logger():
logger = logging.getLogger("mylogger")
logger.setLevel(logging.DEBUG)

fh = logging.FileHandler("threading.log")
fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
fh.setFormatter(formatter)

logger.addHandler(fh)
return logger
def tom(number, logger):

if __name__ == "__main__":
logger = get_logger()
for i in range(5):
th = mythread(i, logger)
th.start()

在 tom 函数中,我们把 print 语句换成 logging 语句。你会注发现,在创建线程时,我们给 doubler 函数传入了 logger 对象。这样做的原因是,如果在每个线程中实例化 logging 对象,那么将会产生多个 logging 单例(singleton),并且日志中将会有很多重复的内容


线程锁与线程同步

由于物理上得限制,各CPU厂商在核心频率上的比赛已经被多核所取代。为了更有效的利用多核处理器的性能,就出现了多线程的编程方式,而随之带来的就是线程间数据一致性和状态同步的困难。解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。锁由 Python 的 threading 模块提供,并且它最多被一个线程所持有。当一个线程试图获取一个已经锁在资源上的锁时,该线程通常会暂停运行,直到这个锁被释放。

有两种方式为线程加锁:



  1. try...finally


  2. with


代码如下:

import threading
import logging
lock = threading.Lock()
class mythread(threading.Thread):
def __init__(self,number,logger):
threading.Thread.__init__(self)
self.number = number
self.logger = logger

def run(self):
lock.acquire()
try:
self.logger.debug("calling-thread")
tom(self.number, self.logger)
finally:
lock.release()
def get_logger():
logger = logging.getLogger("mylogger")
logger.setLevel(logging.DEBUG)

fh = logging.FileHandler("threading.log")
fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
fh.setFormatter(formatter)

logger.addHandler(fh)
return logger
def tom(number, logger):
with lock:
logger.debug(number)
if __name__ == "__main__":
logger = get_logger()
for i in range(5):
with lock:
th = mythread(i, logger)
th.start()

当你真正运行这段代码时,你会发现它只是挂起了。究其原因,是因为我们只告诉 threading 模块获取锁。所以当我们调用第一个函数时,它发现锁已经被获取,随后便把自己挂起了,直到锁被释放,然而这将永远不会发生。

真正的解决办法是使用重入锁(Re-Entrant Lock)。threading 模块提供的解决办法是使用 RLock 函数。即把 lock = threading.lock() 替换为 lock = threading.RLock(),然后重新运行代码,现在代码就可以正常运行了。


线程通信

某些情况下,你会希望线程之间互相通信。就像先前提到的,你可以通过创建 Event 对象达到这个目的。但更常用的方法是使用队列(Queue)。在我们的例子中,这两种方式都会有所涉及。下面让我们看看到底是什么样子的:

import threading
import Queue
def creator(data, q):
"""
生成用于消费的数据,等待消费者完成处理
"""
print('Creating data and putting it on the queue')
for item in data:
evt = threading.Event()
q.put((item, evt))

print('Waiting for data to be doubled')
evt.wait()
def my_consumer(q):
"""
消费部分数据,并做处理
这里所做的只是将输入翻一倍
"""
while True:
data, evt = q.get()
print('data found to be processed: {}'.format(data))
processed = data * 2
print(processed)
evt.set()
q.task_done()
if __name__ == '__main__':
q = Queue()
data = [5, 10, 13, -1]
thread_One= threading.Thread(target=creator, args=(data, q))
thread_two = threading.Thread(target=my_consumer, args=(q,))
thread_one.start()
thread_two.start()

q.join()

让我们掰开揉碎分析一下。首先,我们有一个创建者(creator)函数(亦称作生产者(producer)),我们用它来创建想要操作(或者消费)的数据。然后用另外一个函数 my_consumer 来处理刚才创建出来的数据。Creator 函数使用 Queue 的 put 方法向队列中插入数据,消费者将会持续不断的检测有没有更多的数据,当发现有数据时就会处理数据。Queue 对象处理所有的获取锁和释放锁的过程,这些不用我们太关心。

在这个例子中,先创建一个列表,然后创建两个线程,一个用作生产者,一个作为消费者。你会发现,我们给两个线程都传递了 Queue 对象,这两个线程隐藏了关于锁处理的细节。队列实现了数据从第一个线程到第二个线程的传递。当第一个线程把数据放入队列时,同时也传递一个 Event 事件,紧接着挂起自己,等待该事件结束。在消费者侧,也就是第二个线程,则做数据处理工作。当完成数据处理后就会调用 Event 事件的 set 方法,通知第一个线程已经把数据处理完毕了,可以继续生产了。

最后一行代码调用了 Queue 对象的 join 方法,它会告知 Queue 等待所有线程结束。当第一个线程把所有数据都放到队列中,它也就运行结束了。


推荐阅读
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式
    大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式 ... [详细]
  • 使用多项式拟合分析淘宝双11销售趋势
    根据天猫官方数据,2019年双11成交额达到2684亿元,再次刷新历史记录。本文通过多项式拟合方法,分析并预测未来几年的销售趋势。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • 检查在所有可能的“?”替换中,给定的二进制字符串中是否出现子字符串“10”带 1 或 0 ... [详细]
  • 解决问题:1、批量读取点云las数据2、点云数据读与写出3、csf滤波分类参考:https:github.comsuyunzzzCSF论文题目ÿ ... [详细]
  • 第二十五天接口、多态
    1.java是面向对象的语言。设计模式:接口接口类是从java里衍生出来的,不是python原生支持的主要用于继承里多继承抽象类是python原生支持的主要用于继承里的单继承但是接 ... [详细]
  • 本题探讨如何编写程序来计算一个数值的整数次方,涉及多种情况的处理。 ... [详细]
  • 如何将Python与Excel高效结合:常用操作技巧解析
    本文深入探讨了如何将Python与Excel高效结合,涵盖了一系列实用的操作技巧。文章内容详尽,步骤清晰,注重细节处理,旨在帮助读者掌握Python与Excel之间的无缝对接方法,提升数据处理效率。 ... [详细]
  • 属性类 `Properties` 是 `Hashtable` 类的子类,用于存储键值对形式的数据。该类在 Java 中广泛应用于配置文件的读取与写入,支持字符串类型的键和值。通过 `Properties` 类,开发者可以方便地进行配置信息的管理,确保应用程序的灵活性和可维护性。此外,`Properties` 类还提供了加载和保存属性文件的方法,使其在实际开发中具有较高的实用价值。 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 本文对比了杜甫《喜晴》的两种英文翻译版本:a. Pleased with Sunny Weather 和 b. Rejoicing in Clearing Weather。a 版由 alexcwlin 翻译并经 Adam Lam 编辑,b 版则由哈佛大学的宇文所安教授 (Prof. Stephen Owen) 翻译。 ... [详细]
  • 本文详细解析了客户端与服务器之间的交互过程,重点介绍了Socket通信机制。IP地址由32位的4个8位二进制数组成,分为网络地址和主机地址两部分。通过使用 `ipconfig /all` 命令,用户可以查看详细的IP配置信息。此外,文章还介绍了如何使用 `ping` 命令测试网络连通性,例如 `ping 127.0.0.1` 可以检测本机网络是否正常。这些技术细节对于理解网络通信的基本原理具有重要意义。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
author-avatar
书友16941424_529
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有