乐趣区

python线程

线程模块

Python标准库提供了两个线程模块:_threadthreading_thread 提供了低级别的、原始的线程以及一个简单的互斥锁,相比 threading 模块的功能还是比较有限的。threading模块是 _thread 模块的替代,在实际开发中,绝大多数情况下还是使用高级模块threading

创建 thread 对象语法如下:

import threading

threading.Thread(target=None, name=None, args=())

主要参数说明:

target是函数名字,需要调用的函数。
name设置线程名字。
args是函数需要的参数,以元祖的形式传入。

主要方法说明:

run(): 用以表示线程活动的方法。
start(): 启动线程活动。
join(): 等待至线程中止。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。

函数式创建线程

创建线程的时候,只需要传入一个执行函数和函数的参数即可。下面的例子使用 thread 类来产生 2 个子线程,然后启动 2 个子线程并等待其结束,

#!/usr/bin/python
# -*- coding: UTF-8 -*-


import threading
import time,random,math
 

def print_num(idx):
    for num in range(idx):
        print("{0}\tnum={1}".format(threading.current_thread().getName(), num))
        delay = math.ceil(random.random() * 2)
        time.sleep(delay)
 
if __name__ == '__main__':
    th1 = threading.Thread(target=print_num, args=(2,), name="test1")
    th2 = threading.Thread(target=print_num, args=(3,), name="test2")

    th1.start()
    th2.start()

    th1.join()
    th2.join()
    print("{0} over".format(threading.current_thread().getName()))

运行结果如下:

$ python thread1.py 
test1    num=0
test2    num=0
test1    num=1
test2    num=1
test2    num=2
MainThread over

运行脚本默认会启动一个线程,把该线程称为主线程,主线程又启动新的线程。Pythonthreading 模块有个 current_thread() 函数,它将返回当前线程的实例,可以获得运行线程名字,代码如下:

threading.current_thread().getName()

启动一个线程就是把一个函数和参数传入并创建 Thread 实例,然后调用 start() 开始执行:

th1 = threading.Thread(target=print_num, args=(2,), name="test1" )
th1.start()

从返回结果可以看出主线程的名字叫 MainThread,子线程的名字在创建时指定。如果没有给线程起名字,Python 就自动给线程命名为 Thread-1Thread-2 等等。

创建线程类

通过继承 Thread 类,并重写 Thread 类的 run() 方法,在 run() 方法中定义具体要执行的任务。在 Thread 类中,提供了一个 start() 方法用于启动新线程,线程启动后会自动调用 run() 方法。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading
import time,random,math


class MutliThread(threading.Thread):
    def __init__(self, threadName, num):
        threading.Thread.__init__(self)
        self.name     = threadName
        self.num     = num
 
    def run(self):
        for i in range(self.num):
            print("{0} i={1}".format(threading.current_thread().getName(), i))
            delay = math.ceil(random.random() * 2)
            time.sleep(delay)

if __name__ == '__main__':
    thr1 = MutliThread("test1", 3)
    thr2 = MutliThread("test2", 2)

    thr1.start()
    thr2.start()

    thr1.join()
    thr2.join()
    print("{0} over".format(threading.current_thread().getName()))

运行结果如下:

$ python thread2.py 
test1 i=0
test2 i=0
test1 i=1
test2 i=1
test1 i=2
MainThread over

从返回结果可以看出,通过创建 Thread 类来产生 2 个线程对象 thr1thr2,重写 Thread 类的 run() 函数,把业务逻辑放入其中,通过调用线程对象的 start() 方法启动线程。通过调用线程对象的 join() 函数,等待该线程完成。

守护线程

在线程模块中,使用子线程对象用到 join() 函数,主线程需要依赖子线程执行完毕后才继续执行。如果不使用 join() 函数,主线程和子线程是并行运行的,没有依赖关系。

在多线程开发中,如果子线程设定为守护线程,守护线程会等待主线程运行完毕后被销毁。一个主线程可以设置多个守护线程,守护线程运行的前提是主线程必须存在,如果主线程不存在了,守护线程会被销毁。

以下例子创建了 1 个主线程和 3 个子线程,让主线程和子线程并行执行,代码如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading, time


def run(taskName):
    print("task:", taskName)
    time.sleep(2)
    print("{0} over".format(taskName))
 
if __name__ == '__main__':
    start_time = time.time()
    for i in range(3):
        thr = threading.Thread(target=run, args=("task-{0}".format(i),))
        thr.start()

    print("{0} over,thread_num={1}".format(threading.current_thread().getName(), threading.active_count()))
    print("cost time:", time.time() - start_time)

运行结果如下:

$ python thread3.py 
('task:', 'task-0')
('task:', 'task-1')
('task:', 'task-2')
MainThread over,thread_num=4
('cost time:', 0.0011091232299804688)
task-0 over
task-1 over
task-2 over

从返回结果可以看出,当前的线程个数是4,主线程执行完毕后,等待子线程执行完毕,程序才会退出。

在以上例子的基础上,使用线程对象的 setDaemon(True) 函数把所有的子线程都设置为守护线程。子线程变成守护线程后,只要主线程执行完毕,程序不管子线程有没有执行完毕,程序都会退出。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading, time
 
def run(taskName):
    print("task:", taskName)
    time.sleep(2)
    print("{0} over".format(taskName))
 
if __name__ == '__main__':
    start_time = time.time()
    for i in range(3):
        thr = threading.Thread(target=run, args=("task-{0}".format(i),))
        thr.setDaemon(True)
        thr.start()

    thrName  = threading.current_thread().getName()
    thrCount = threading.active_count()

    print("{0} over,thread_num={1}".format(thrName, thrCount))
    print("cost time:", time.time() - start_time)

运行结果如下:

$ python thread4.py 
('task:', 'task-0')
('task:', 'task-1')
('task:', 'task-2')
MainThread over,thread_num=4
('cost time:', 0.0005180835723876953)

从返回结果可以看出,主线程执行完毕后,程序不会等待守护线程执行完毕就退出了。设置线程对象为守护线程,一定要在线程对象调用 start() 函数前设置。

退出移动版