Python中的多线程笔记

Python多线程用法

基本用法

#!/usr/bin/python
# -*- 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")
 

获取线程函数执行结果

线程搭载的函数不一定是无返回值的,对于需要获取返回值的多线程任务我们应该怎么做呢?
Python自带的Thread类是无法实现这个功能的,最合理的解决方案是继承Thread类,用自定义的Thread类维护函数执行结果,示例代码如下:

import threading,time
class ThreadWithResult(threading.Thread):
    def __init__(self, func, args=()):
        super(ThreadWithResult, self).__init__()
        self.func = func
        self.args = args
 
    def run(self):
        time.sleep(1)
        self.result = self.func(*self.args)
 
    def get_result(self):
        threading.Thread.join(self) # get_result 自带join
        try:
            return self.result
        except Exception:
            return None

这个Thread的子类当中实现了get_result()方法,这个方法包含了join(),因此这个线程启动后,如果要主线程等待子线程结束,只需要调用get_result()即可,不需要再join()这个线程。使用方法如下:


# 接之前的代码
def add (a, b):
    return a + b
 
if __name__=="__main__":
    data=[[1,2],[3,4],[5,6]]
    thread_list = []
    sum_result = 0
    for i in range(3):
        t= ThreadWithResult(add, (data[i][0], data[i][1]))
        t.start()
        thread_list.append(t)
    for t in thread_list:
   		sum_result+=t.get_result()
    print(f"Sum:{sum_result}")

Python多线程无法利用多核CPU计算性能

在使用Python的多线程之前需要先明确一个概念:Python的多线程并不是完整的多线程!
Python本质上还是一个解释型的语言(这也是为什么python性能不太好的一个原因),Python代码的执行由Python虚拟机(也被称为解释器)来控制。
Python在设计之初有一个避免冲突的考虑,那就是在解释器中同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样地,虽然Python解释器可以运行多个线程,只有一个线程在解释器中运行。
Python虚拟机某一个时刻执行那条代码是一个排斥性的机制,因此需要用锁来控制,目前的Python虚拟机实现的机制是提供一个全局解释器锁(GIL),拿到这个锁的代码才能在解释器中运行,也就是说这个锁限制了一个Python虚拟机中同时只有一个线程在运行。
在Python的多线程环境中,Python虚拟机在线程间切换的机制主要分为以下几步:

  1. 获取GIL锁。
  2. 切换到一个线程去执行。
  3. 运行。
  4. 把线程设置为睡眠状态。
  5. 解锁GIL。
  6. 再次重复以上步骤。

在单核系统中,这个机制没有任何问题,但是一旦到了多核cpu的时代,这个机制就开始严重拖python性能后腿。其他语言如c,c++可以只启动一个进程,然后在这个进程中创建多个线程,再利用多线程调度机制充分利用多核cpu的性能。然而在Python中,如果你只启动一个python虚拟机,那么不管你有几个核,这个虚拟机只能利用起来一个核心,然后用时间片轮转的方式来切换线程,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。这是GIL机制的历史遗留问题。通常情况下我们用的解释器是官方实现的CPython,如果执意要用python多线程来利用多核的性能,那就只能自己实现一个不用GIL的解释器,估计没什么人会这么做吧。

Python多线程的意义

看完上一节,估计有人会问,既然python多线程无法利用起来多核cpu计算性能,线程上下文切换还要浪费资源,那么Python多线程还有什么意义?
一个程序不止有计算需要消耗性能,还有IO性能的消耗,有的IO操作是非常耗时的,如网络连接,对于IO密集型程序,就可以利用多线程避免单线程的阻塞。最典型的IO密集型程序就是web应用,这种应用对计算性能需求不大,因此可以用多线程机制创建数十乃至数千的线程来处理web请求,这样即使只有单核也可以同时处理大量的web请求。
不过现在很多语言新的feature使得比线程更轻量级的协程称为可能,协程甚至不需要维护线程栈,几乎毫无上下文切换的开销,是IO密集型程序的首选,python也可以做到在单线程内用yeild语句实现协程,具体实现方法未来专门写一篇博客来讲。

如何利用多核CPU计算性能

那我们如果遇到计算密集的任务怎么办?我们就没法利用多核cpu了吗?如果要在python中利用多核cpu应该怎么办?
那就是更加笨重的多进程了,不过已经用python了,多进程增加的负担也不算什么了吧~
python提供了自带的多进程库 Multiprocessing,python多进程将在另一篇博客当中做学习。
但是最根本的建议是:如果真的要在工业界做计算密集的任务不要用python来做

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值