设为首页 - 加入收藏 平凉站长网 (http://www.0933zz.com)- 国内知名站长资讯网站,提供最新最全的站长资讯,创业经验,网站建设等!
热搜: 什么 为什么 赚钱 之前
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

一篇文章读懂 Python 多线程

发布时间:2019-09-24 18:29 所属栏目:[优化] 来源:编程派
导读:Threading模块从 Python 1.5.2 版开始出现,用于增强底层的多线程模块thread。Threading 模块让操作多线程变得更简单,并且支持程序同时运行多个操作。 注意,Python 中的多线程最好用于处理有关 I/O 的操作,如从网上下载资源或者从本地读取文件或者目录

Threading模块从 Python 1.5.2 版开始出现,用于增强底层的多线程模块thread。Threading 模块让操作多线程变得更简单,并且支持程序同时运行多个操作。

一篇文章读懂 Python 多线程

注意,Python 中的多线程最好用于处理有关 I/O 的操作,如从网上下载资源或者从本地读取文件或者目录。如果你要做的是 CPU 密集型操作,那么你需要使用 Python 的multiprocessing模块。这样做的原因是,Python 有一个全局解释器锁 (GIL),使得所有子线程都必须运行在同一个主线程中。正因为如此,当你通过多线程来处理多个 CPU 密集型任务时,你会发现它实际上运行的更慢。因此,我们将重点放在那些多线程最擅长的领域:I/O 操作!

线程简介

多线程能让你像运行一个独立的程序一样运行一段长代码。这有点像调用子进程(subprocess),不过区别是你调用的是一个函数或者一个类,而不是独立的程序。在我看来,举例说明更有助于解释。下面来看一个简单的例子:

  1. import?threading?
  2. ?
  3. def?doubler(number):?
  4. """?
  5. 可以被线程使用的一个函数?
  6. """?
  7. print(threading.currentThread.getName?+?'\n')?
  8. print(number?*?2)?
  9. print?
  10. ?
  11. if?__name__?==?'__main__':?
  12. for?i?in?range(5):?
  13. my_thread?=?threading.Thread(target=doubler,?args=(i,))?
  14. my_thread.start?

这里,我们导入 threading 模块并且创建一个叫 doubler的常规函数。这个函数接受一个值,然后把这个值翻一番。它还会打印出调用这个函数的线程的名称,并在最后打印一行空行。然后在代码的最后一块,我们创建五个线程并且依次启动它们。在我们实例化一个线程时,你会注意到,我们把 doubler 函数传给target参数,同时也给 doubler 函数传递了参数。Args参数看起来有些奇怪,那是因为我们需要传递一个序列给 doubler 函数,但它只接受一个变量,所以我们把逗号放在尾部来创建只有一个参数的序列。

需要注意的是,如果你想等待一个线程结束,那么需要调用 join方法。

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

  1. Thread-1?
  2. ?
  3. 0?
  4. ?
  5. Thread-2?
  6. ?
  7. 2?
  8. ?
  9. Thread-3?
  10. ?
  11. 4?
  12. ?
  13. Thread-4?
  14. ?
  15. 6?
  16. ?
  17. Thread-5?
  18. ?
  19. 8?

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

  1. import?logging?
  2. import?threading?
  3. ?
  4. def?get_logger:?
  5. logger?=?logging.getLogger("threading_example")?
  6. logger.setLevel(logging.DEBUG)?
  7. ?
  8. fh?=?logging.FileHandler("threading.log")?
  9. fmt?=?'%(asctime)s?-?%(threadName)s?-?%(levelname)s?-?%(message)s'?
  10. formatter?=?logging.Formatter(fmt)?
  11. fh.setFormatter(formatter)?
  12. ?
  13. logger.addHandler(fh)?
  14. return?logger?
  15. ?
  16. def?doubler(number,?logger):?
  17. """?
  18. 可以被线程使用的一个函数?
  19. """?
  20. logger.debug('doubler?function?executing')?
  21. result?=?number?*?2?
  22. logger.debug('doubler?function?ended?with:?{}'.format(?
  23. result))?
  24. ?
  25. if?__name__?==?'__main__':?
  26. logger?=?get_logger?
  27. thread_names?=?['Mike',?'George',?'Wanda',?'Dingbat',?'Nina']?
  28. for?i?in?range(5):?
  29. my_thread?=?threading.Thread(?
  30. target=doubler,?name=thread_names[i],?args=(i,logger))?
  31. my_thread.start?

代码中最大的改变就是加入了 get_logger函数。这段代码将创建一个被设置为调试级别的日志记录器。它将日志保存在当前目录(即脚本运行所在的目录)下,然后设置每行日志的格式。格式包括时间戳、线程名、日志记录级别以及日志信息。

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

【免责声明】本站内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

网友评论
推荐文章