Threading -- 简单高效的Python多线程利器

给Python加速

在日常的工作学习中,有时候会遇到计算量很大的情况,这时候就要用到多线程来提高工作效率。
众所周知,Python是一门上手简单功能强大的语言,但是计算效率相对来说就比较“低”了,有时候一个程序要跑好久才能完成,这无疑是很难受的。那么提高Python程序运行速度就非常必须了。
要提升Python运行效率方法很多,比如可以在Python中调用C/C++,让C/C++来干“重活”,Python调用C/C++可参考我的这篇文章
除此之外,还可以将单线程的Python程序改成多线程来提升运行效率。

Python多线程

在Python中,实现多线程的方法也有很多种,主要的方法有:

  • 利用_Thread模块
  • 利用Threading模块
  • 利用Queue模块
  • 利用multiprocessing模块
    除了上面列出来的之外还有其他模块也可以实现多线程,更多的内容可以参考《Python核心编程》一书。

在上面列举的模块中,_ThreadThreading模块是最简单和常用的模块。不过,从名字中也可以看出,_Thread不推荐使用,因为此模块有部分属性与threading冲突,还有Thread模块对于程序退出没有控制,主线程结束时,子线程也强制结束并没有警告,这不讲武德。所以建议使用Threading模块或者其他安全的模块。

threading模块

Threading模块中最重要的类就是Thread类,主要使用该类实例化对象来创建多线程。

属性 功能
name 线程的名称
ident 线程标识符
daemon 布尔值,True表示该线程为守护进程
__ init__(group=None, target=None, name=None, args=(),kwargs={}, verbose=None, daemon=None) 实例化一个线程对象,需要一个可调用的target对象,以及参数args或者kwargs。还可以传递name和group参数。daemon的值将会设定thread.daemon的属性
start() 开始执行该线程
run() 在继承Thread类的时候需要重写该方法,在该方法内实现多线程逻辑
join(timeout=None) 等待直到该线程执行完成或者timeout秒

利用Thread类创建多线程的方式主要有3种:

  • 创建Thread实例,将要执行的函数和参数传递给该实例。
  • 创建Thread实例,传递一个可调用的类实例。
  • 继承Thread类,重写run()方法,创建子类实例。
1、传递函数给Thread实例
import threading
import time
def loop_func(i, sec):
	print("This is {} loop.".format(i))
	time.sleep(sec)
	print("The {} loop finished.".format(i))
def main():
	l = [5, 2, 1] #每个函数的循环时间
	ts = []
	for i in range(len(l)):
		thread = threading.Thread(target=loop_func, args=(i, l[i])) #实例化3个Thread对象,target指定调用的函数,args指定传给loop_func的参数
		ts.append(thread)
	for i in range(len(l)):
		ts[i].start() #启动线程
	for i in range(len(l)):
		ts[i].join() #阻塞线程,使得主线程等到所有的子线程完成后再退出结束
	
	print("Main Thread Finished")
if __name__ == "__main__":
	main()

输出结果:

This is 0 loop.
This is 1 loop.
This is 2 loop.
The 2 loop finished.
The 1 loop finished.
The 0 loop finished.
Main Thread Finished

可以看出,线程0-2分别被创建,然后线程2的执行时间最短(1s)所以最先结束输出The 2 loop finished.,线程0的时间最久(5s),是最后的子线程。最后在所有子线程都结束后,主线程才结束,输出Main Thread Finished

如果注释掉

#for i in range(len(l)):
#		ts[i].join() #阻塞线程,使得主线程等到所有的子线程完成后再退出结束

那么程序的输出就是

This is 0 loop.
This is 1 loop.
This is 2 loop.
Main Thread Finished
The 2 loop finished.
The 1 loop finished.
The 0 loop finished.

可以看到,如果没有调用子线程join()方法,那么主线程不会等待子线程结束就直接推出了,Main Thread Finished先于The 2 loop finished.输出。

2、将一个可调用类实例传递给Thread实例

定义一个MyThread类,将loop_func封装到类内。

import threading
import time

class MyThread(object):  #自定义一个类
    def __init__(self, func, args, name=""):
        self.name = name
        self.func = func
        self.args = args
    def __call__(self, *args, **kwargs):
        self.func(*self.args)  #重写类的__call__方法,在该方法内调用要执行的函数
        
def loop_func(i, sec):
	print("This is {} loop.".format(i))
	time.sleep(sec)
	print("The {} loop finished.".format(i))

def main():
    l = [5, 2, 1]
    ts = []
    for i in range(len(l)): #创建len(l)个对象,这里是3个
        mythread = MyThread(loop_func, (i, l[i])) #实例化一个类对象
        thread = threading.Thread(target=mythread)
        ts.append(thread)
    for i in range(len(l)):
        ts[i].start()
    for i in range(len(l)):
        ts[i].join()
    print("Main Thread Finished.")
if __name__ == "__main__":
    main()

输出与方法1一致

This is 0 loop.
This is 1 loop.
This is 2 loop.
The 2 loop finished.
The 1 loop finished.
The 0 loop finished.
Main Thread Finished.
3、继承Thread类,创建子类实例

方法3形式上看起来与2差不多,都是创建一个类然后实例化,不过二者本质其实还是不一样的。
自定义一个类继承自threaading.Thread类,需要重写子类的run()方法

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, func, args, name=""):
        super(MyThread, self).__init__()  #初始化父类
        self.name = name
        self.func = func
        self.args = args
    def run(self):   #将方法2的__call__换成run
        self.func(*self.args)  #重写子类的run方法,在该方法内调用要执行的函数
        
def loop_func(i, sec):
	print("This is {} loop.".format(i))
	time.sleep(sec)
	print("The {} loop finished.".format(i))

def main():
    l = [5, 2, 1]
    ts = []
    for i in range(len(l)): #创建len(l)个对象,这里是3个
        mythread = MyThread(loop_func, (i, l[i])) #实例化一个子类对象,这里的子类对象就是一个子线程,与方法2的子类对象本质不一样。
        #thread = threading.Thread(target=mythread) #不需要
        ts.append(mythread)
    for i in range(len(l)):
        ts[i].start()
    for i in range(len(l)):
        ts[i].join()
    print("Main Thread Finished.")
if __name__ == "__main__":
    main()

运行结果如下所示:

This is 0 loop.
This is 1 loop.
This is 2 loop.
The 2 loop finished.
The 1 loop finished.
The 0 loop finished.
Main Thread Finished.

与上面两种方法结果一致。

至于这3种方法哪种比较好,需要看使用场景,如果追求简单第一种方法最合适,如果追求可复用性和面向对象我认为第3种最好,第2种也可。

写了这么多,最基本的多线程Threading使用介绍得差不多了,基本上也够我用了,更多的细节可以参考《Python核心编程》或者以下链接。

参考

https://www.cnblogs.com/hiwuchong/p/8673183.html
https://www.cnblogs.com/lht-record/p/8343876.html

猜你喜欢

转载自blog.csdn.net/weixin_40313940/article/details/111234644
今日推荐