Python并发编程—多线程threading

threading对象

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

参数

group: 默认None,保留
target(): 由run()方法调用的可调用对象
name: 自定义线程名称
args: 目标调用的参数元组
kwargs: 目标调用的关键字参数的字典

方法

start():启动线程
run(): 表示线程活动的方法。启动线程后,调用可调用对象
join(timeout=None): 阻塞线程,直到执行结束或超时
is_alive(): 返回线程是否存活

属性

name: 可读写,线程名
ident: 只读,线程id
daemon: 可读写, 是否守护线程。设置为true,说明该线程是不重要的,则主线程不会阻塞到该线程执行完毕

import threading
from time import sleep


def task():
    print("thread_start")
    print(threading.current_thread().name)
    print(threading.current_thread().ident)
    print(threading.current_thread().daemon)
    sleep(2)


if __name__ == "__main__":
    t = threading.Thread(target=task)
    t.name = 'hello thread'
    t.daemon = False
    t.start()
thread_start
hello thread
15600
False

线程锁

Lock

acquire(blocking=True, timeout=-1): 有两种方式获取锁,阻塞方式和非阻塞方式
阻塞方式,需要等待锁释放后才能加锁,然后返回True
非阻塞方式,已经被锁定,则直接返回False,不等待,如果未被所得,则锁定,并返回True
锁定方法acquire可以有一个超时时间的可选参数timeout。如果设定了timeout,则在超时后通过返回值可以判断是否得到了锁,从而可以进行一些其他的处理

release(): 释放锁,这可以从任何线程调用,而不仅仅是已经获得锁的线程。

RLock

可重入锁,同一线程中可以多次锁定,acquire和release必须成对出现

import threading

lock = threading.RLock() # 可重入锁,能嵌套锁,此处如果使用Lock,程序会死锁,阻塞


def fun_a():
    with lock:
        fun_b()


def fun_b():
    with lock:
        fun_c()


def fun_c():
    print('1')


if __name__ == "__main__":
    threading.Thread(target=fun_a).start()

Condition

方法

acquire():获取锁
release():释放锁
wait(timeout=None):等待直到通知或直到发生超时。
wait_for(predicate, timeout=None):等待predicate结果为true
notify(n=1):唤醒等待此条件的一个线程
notify_all():唤醒所有等待此条件的线程

import threading
from queue import Queue
from time import sleep

con = threading.Condition()
q = Queue()


def fun_a(a):
    sleep(1)          # 等待fun_b进入等待状态,避免fun_a先发出通知,fun_b再进入等待状态,就会阻塞
    con.acquire()     # 获取锁
    while a < 10:
        a += 1
    else:
        con.notify()  # 发出通知,唤醒fun_b
        q.put(a)
    con.release()     # 释放锁


def fun_b():
    con.acquire()     # 获取锁
    con.wait()        # 等待通知
    print(q.get())
    con.release()     # 释放锁


if __name__ == "__main__":
    threading.Thread(target=fun_a, args=(20,)).start()
    threading.Thread(target=fun_b).start()

Semaphore/BoundedSemaphore

信号量对象
Semaphore 在内部管理着一个计数器。调用 acquire() 会使这个计数器 -1,release() 则是+1。当计数器到 0 时,再调用 acquire() 就会阻塞,直到其他线程来调用release()。如果多个 acquire() 呼叫被阻塞,release() 将只唤醒其中一个,随机选择一个。
BoundedSemaphore:类似于Semaphore;不同在于BoundedSemaphore 会检查内部计数器的值,并保证它不会大于初始值,信号量用于保护有限容量的资源。

from multiprocessing.dummy import Pool
from threading import Semaphore, current_thread
import time


def fun_1(a):
    semaphore.acquire()
    print(a, current_thread().name, time.time())
    time.sleep(2)
    semaphore.release() 
    semaphore.release() # 多次释放后,同时执行的线程数会增多


if __name__ == "__main__":
    semaphore = Semaphore(2) # 信号值2,同时只有两个线程运行
    num_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    with Pool(6) as pool: # 线程池,6个线程,但启动时开始只有两个线程同时执行
        pool.map_async(fun_1, num_list).get()
1 Thread-1 1542905541.487621 
2 Thread-2 1542905541.487621
7 Thread-1 1542905543.4913213
8 Thread-2 1542905543.4913213
5 Thread-6 1542905543.4913213
4 Thread-3 1542905543.4913213
9 Thread-3 1542905545.4980922
6 Thread-4 1542905545.4980922
3 Thread-5 1542905545.4980922
### 根据时间戳可以看到第一次只有两个线程同时执行,后面同时执行线程数就多了
from multiprocessing.dummy import Pool
from threading import BoundedSemaphore, current_thread
import time


def fun_1(a):
    semaphore.acquire()
    print(a, current_thread().name, time.time())
    time.sleep(2)
    semaphore.release()
    semaphore.release() # 多次释放后,超出上限会抛异常


if __name__ == "__main__":
    semaphore = BoundedSemaphore(2)
    num_list = [1, 2, 3, 4]
    with Pool(3) as pool:
        pool.map_async(fun_1, num_list).get()
多次释放超出上限,抛出异常
    raise ValueError("Semaphore released too many times")
ValueError: Semaphore released too many times

Event

实现事件对象的类。事件管理一个内部标志flag,可以使用 set() 方法设置标志flag为true并使用 clear() 方法重置为false的标志。 wait() 方法阻塞,直到标志为真。该标志最初为假。一个线程发出事件信号,其他线程等待它。

方法

is_set():返回内部标志状态
set():设置标志flag为true
clear():设置标志flag为true
wait(timeout=None):阻塞直到内部标志为真或超时

from threading import Event, Thread
import time

event = Event()


def fun_a():
    print('fun_a', time.time())
    time.sleep(3)
    event.set()


def fun_b():
    # 阻塞至fun_a把标志设为True才开始执行,3s后打印
    event.wait()
    print('fun_b', time.time())


def fun_c():
    # 等待1s后,打印
    time.sleep(1)
    print('fun_c', time.time())


if __name__ == "__main__":
    Thread(target=fun_a).start()
    Thread(target=fun_b).start()
    Thread(target=fun_c).start()
fun_a 1543078891.0669804
fun_c 1543078892.0691493
fun_b 1543078894.0687258

Timer

等待一定时间后,执行线程

方法

cancel(): 停止定时器,并取消定时器动作的执行。这只有在定时器仍处于等待阶段时才工作。

Barrier

这个类提供了一个简单的同步原语,供需要彼此等待的固定数量的线程使用。线程等待数量达到设定数量,冲破屏障

方法

wait(timeout=None):线程等待通过障碍
reset():重置屏障状态
abort():强行打破屏障

属性

parties: 通过屏障所需等待数量
n_waiting: 等待中的数量
broken: 屏障处于破碎状态返回True

from threading import Barrier, Thread
import time


def fun_a():
    print('fun_a', time.time())


barrier = Barrier(3, fun_a)


def fun_b():
    print('fun_b', time.time())
    barrier.wait()


def fun_c():
    print('fun_c', time.time())
    barrier.wait()


def fun_d():
    time.sleep(2) 
    print('fun_d', time.time())
    barrier.wait()  # 2s后等待线程数为3,冲破barrier,fun_a执行


if __name__ == "__main__":
    Thread(target=fun_b).start()
    Thread(target=fun_c).start()
    Thread(target=fun_d).start()
fun_b 1543079035.0616755
fun_c 1543079035.0616755
fun_d 1543079037.062626
fun_a 1543079037.062626

local

线程本地数据,所有线程共用一个实例,每个线程内部实例值不一样

import threading

a = threading.local()  # 全局对象


def worker(n):
    a.x = n  # 每个线程管理自己内部的数据,互不影响
    for _ in range(10):
        a.x += 1
    print(threading.current_thread(), a.x)


if __name__ == "__main__":
    for i in range(3):
        threading.Thread(target=worker, args=(i,)).start()
<Thread(Thread-1, started 16160)> 10
<Thread(Thread-2, started 15568)> 11
<Thread(Thread-3, started 13496)> 12

其他

current_thread(): 返回当前活动的 Thread 对象的数量
active_count(): 返回当前 Thread 对象,对应于调用者的控制线程。
enumerate(): 返回当前活动的所有 Thread 对象的列表。
main_thread(): 返回主 Thread 对象。
setprofile(): 为从 threading 模块启动的所有线程设置配置文件功能。
settrace(): 为从 threading 模块启动的所有线程设置跟踪功能。
stack_size(): 返回创建新线程时使用的线程堆栈大小。