一个进程可以包含多个线程,而且至少必须要有一个线程,这个线程被称为主线程,在Python中的名字为 MainThread。进程与线程最大的不同在于,进程的信息时各自的,而(同一个进程下的)线程的信息是公用的,即他们都处理、使用所在进程的信息数据等。

可以使用Python中的threading模块进行线程管理于操作。使用 threading.current_thread() 获取当前所在线程,使用线程实例的name属性获取线程的名字。

使用Thread创建多线程

一个python运行起来后对应着一个进程,这个进程的的主线程即 MainThread,使用Thread可以创建新的线程,创建时使用参数 target 绑定线程运行的目标,使用 name 参数为线程定义一个名字。

Thread的实例有的常用方法:

  • start:开始执行这个线程
  • join:等待线程执行结束

一个例子:

import time, random, threading


def print_time():
    time.sleep(random.random())
    print("I'm thread {}, time now is {}".format(threading.current_thread().name, time.time()))


print("The main thread is {}".format(threading.current_thread().name))
thread1 = threading.Thread(target=print_time, name="New Thread 1")
thread2 = threading.Thread(target=print_time, name="New Thread 2")
thread3 = threading.Thread(target=print_time, name="New Thread 3")

thread1.start()
thread2.start()
thread3.start()

thread1.join()
thread2.join()
thread3.join()

得到的结果是随机的:

The main thread is MainThread
I'm thread New Thread 3, time now is 1607256751.4239407
I'm thread New Thread 1, time now is 1607256751.8748183
I'm thread New Thread 2, time now is 1607256751.9063895

使用锁Lock修改数据

由于(同一个进程下的)多个线程使用的是同一套数据,所以如果多个线程同时访问一个数据就会造成冲突,解决的方法是使用线程锁,在某个线程修改这个数据的时候,它获得一把锁,只有获得锁的这个线程有权利修改这个数据,等修改完成后再释放锁,这样就可以保证在一个时间只有一个线程修改这个数据。如果多个线程都想请求得到锁,他们会排队获得。

Python中使用threading.Lock来完成,一个Lock实例常用的的方法有:

  • acquire:请求获得锁
  • relaese:释放锁

为例确保一个线程在使用完后一定会释放锁,通常使用try...final...语句,将释放锁的代码放在finally块里。

一个例子:一个线程要给一个数据加一,一个线程要给同一个数据减一:

import time, random, threading

number = 0
lock = threading.Lock()


def add_one():
    global number
    for i in range(20):
        lock.acquire()
        print("I'm thread {}, I acquired the lock.".format(threading.current_thread().name))
        try:
            number += 1
        finally:
            lock.release()
            print("I'm thread {}, I realised the lock.".format(threading.current_thread().name))


def minus_one():
    global number
    for i in range(20):
        lock.acquire()
        print("I'm thread {}, I acquired the lock.".format(threading.current_thread().name))
        try:
            number -= 1
        finally:
            lock.release()
            print("I'm thread {}, I realised the lock.".format(threading.current_thread().name))


thread_add = threading.Thread(target=add_one, name="Thread ADD")
thread_minus = threading.Thread(target=minus_one, name="Thread MINUS")

thread_add.start()
thread_minus.start()

thread_add.join()
thread_minus.join()

使用ThreadLocal创建进程局部变量

设想如果有多个线程,它们都要处理自己的一个变量,但是又想进行对于该变量的信息交换,这样的情况下,可以使用一个全局变量来记录每个线程的局部变量(比如一个字典),显然是很麻烦的,于是可以使用ThreadLocal来处理线程的局部变量。

一个例子:两个线程都有各自的变量number

import threading

local_variable = threading.local()


def print_number():
    number = local_variable.number
    print("I'm thread {}, I'm processing my number {}".format(threading.current_thread().name, number))


def set_number(num):
    local_variable.number = num
    print_number()


thread1 = threading.Thread(target=set_number, args=(1,), name="Thread 1")
thread2 = threading.Thread(target=set_number, args=(2,), name="Thread 2")

thread1.start()
thread2.start()
thread1.join()
thread2.join()

得到的结果如下:

I'm thread Thread 1, I'm processing my number 1
I'm thread Thread 2, I'm processing my number 2