1、创建线程 方法 一
import time
import threading
def countdown(n): # 先定义一个给线程调用的函数——倒计时函数。
print('子线程名字:{}'.format(threading.current_thread().name)) # 获取当前线程对象的名字。
while n > 0:
print('T-minus:{}'.format(n))
n -= 1
time.sleep(1)
t = threading.Thread(target=countdown, args=(6,), name='线程名字-1', daemon=False)
# 实例化一个线程。
# 参数daemon 默认为False。如果为True, 则主线程结束后,子线程也自动结束。
# 如果子线程用了join()方法,那么daemon这个参数对线程来说,基本上没什么用了,
# 因为主线程会一直阻塞,等待子线程先运行结束。
t.start() # 启动线程。
# t.join() # 利用jion()使主线程阻塞,等待子线程完成后,才能继续运行下面的代码。
time.sleep(2) # 让主线程先暂停两秒,先让子线程执行两秒。
print('开始主线程代码……')
print('子线程是否在运行:{}'.format(t.is_alive())) # t.is_alive() 检查子线程的状态,返回True或False。
print('子线程名字:{}'.format(t.name)) # t.name 获取对象为t线程的名字。
print('当前活动线程的个数:{}'.format(threading.active_count())) # 获取当前活动线程的个数: threading.active_count()
print('主—当前线程的名字:{}'.format(threading.current_thread())) # 获取当前线程对象。
2 创建线程 方法 二
from threading import Thread
import time
# 继承Thread类来新建一个线程。
class CountdownThread(Thread):
def __init__(self, n):
super().__init__() # 继承父类的初始化方法。
self.n = n
def run(self): # 改写run方法。 因为start方法会调用run方法。
while self.n > 0:
print('T-minus', self.n)
self.n -= 1
time.sleep(1)
c = CountdownThread(5)
c.start()
3、 创建线程 方法 三
这个方法也是一个可以在主线程中,终止子线程运行的办法。
因为一般子线程运行后,是无法在主线程中去终止它的,很可能会出现无法预料的错误。
from threading import Thread
import time
class CountdownTask: # 定义一个可以终止线程的类。
def __init__(self):
self._running = True
def terminate(self):
self._running = False # 改变 self._running 为False 来终止子线程。
def run(self, n):
while self._running and n > 0:
print('T-minus', n)
n -= 1
time.sleep(1)
c = CountdownTask()
t = Thread(target=c.run, args=(10,))
t.start()
time.sleep(3)
c.terminate() # 这样在主线程中,就能 用 terminate 方法来终止线程了。
t.join()
4、线程加锁方法 一
import time, threading
# 假定这是你的银行存款:
balance = 0
lock = threading.Lock() # 先生成一个锁的对象。
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
lock.acquire() # 在运行 change_it(n) 前,先要获取锁。
try:
change_it(n)
finally: # 改完了一定要释放锁:
lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
5、线程加锁方法 二
利用上下文管理器 with
import time, threading
# 假定这是你的银行存款:
balance = 0
lock = threading.Lock()
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
with lock: # 会自动获取锁lock.acquire(),退出时释放锁 lock.release()。
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
6、变量传递方法 一
用 线程安全的 字典 来传递,来分隔每个线程 同个名称 不同的变量值。
import threading
global_dict = {} # 定义一个全局变量的字典。
def process_stedunt():
std_name = global_dict[threading.current_thread()]
print('学生:{:3s}, 在 线程 {} 中。'.format(std_name, threading.current_thread()))
def process_thread(name):
global_dict[threading.current_thread()] = name # 通过字典的 键值对来传递,
process_stedunt() # 这样每个线程的变量就不会互相干涉了。
t1 = threading.Thread(target=process_thread, args=('李雷', )) # 线程 1,name 变量的值为‘李雷’
t2 = threading.Thread(target=process_thread, args=('韩梅梅', )) # 线程 2,name 变量的值为‘韩梅梅’
t1.start()
t2.start()
执行结果为:
学生:李雷 , 在 线程 <Thread(Thread-1, started 4688)> 中。
学生:韩梅梅, 在 线程 <Thread(Thread-2, started 13820)> 中。
7、变量传递方法 二 (推荐方法)
利用 threading 中的 local 类来传递
import threading
local_school = threading.local() # 利用 local() 类,先实例化一个对象。
def process_stedunt():
std_name = local_school.student # 读取 local_school.student 的属性值。
print('学生:{:3s}, 在 线程 {} 中。'.format(std_name, threading.current_thread()))
def process_thread(name):
local_school.student = name # 相当于给实例 local_school 一个 name 的属性,并赋值。
process_stedunt()
t1 = threading.Thread(target=process_thread, args=('李雷', ))
t2 = threading.Thread(target=process_thread, args=('韩梅梅', ))
t1.start()
t2.start()
8、利用队列 queue 来进行线程间的通信
import threading
import queue
import time
def wash(dish_list, dish_queue):
for dish in dish_list:
print('{}:开始洗"{}"号盘子。\n'.format(threading.current_thread().name, dish))
time.sleep(1)
dish_queue.put(dish)
#dish_queue.task_done()
def dry(dish_queue):
while True:
dish = dish_queue.get() # 利用get()遍历队列中的元素。
print('{}:正在烘干"{}"号盘子。\n'.format(threading.current_thread().name, dish))
time.sleep(3)
dish_queue.task_done() # 要加上task_done()告诉队列:get()出来的元素处理完毕了。
# 否则后面的 dish_queue.join() 不知道是否处理完毕,就会一直阻塞主线程。
dish_queue = queue.Queue()
dish_list1 = list(range(5))
dish_list2 = list(range(6,15))
dry_thread1 = threading.Thread(target=dry, args=(dish_queue,), name='线程-dry1')
dry_thread2 = threading.Thread(target=dry, args=(dish_queue,), name='线程-dry2')
dry_thread1.start()
dry_thread2.start()
wash_thread = threading.Thread(target=wash, args=(dish_list2, dish_queue), name='线程-wash')
wash_thread.start()
wash(dish_list1, dish_queue) # 留个思考题,这函数如果不是在主线程中调用,也让它在子线程中调用,那么下面的 join()又是怎样一个情况呢???
dish_queue.join() # join()阻塞主线程,等子线程全部遍历了队列中的元素,才继续执行主线程的代码。
print('主线程结束。')
9 threading.Event
threading.Event 可以使一个线程等待其他线程的通知,我们把这个Event传递到线程对象中,Event默认内置了一个标志,初始值为False。一旦该线程通过 wait(timeout) 方法进入等待状态,直到另一个线程调用该Event的set()方法将内置标志设置为True时(或超过wait的等待时间),该线程才会恢复运行。
python线程的事件Event用于主线程控制其他线程的执行,事件主要提供了三个方法wait、clear、set。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。默认为 False。
clear:将“Flag”设置为False,阻塞线程。
set:将“Flag”设置为True,唤醒线程,不再阻塞。
isSet():查询此时的“Flag”状态,返回 True 或者 False
代码示例,把代码文件放入 cmd 中运行,能看到更好的运行效果。
import threading
import itertools
import time, sys
def spin(msg, done): # 在单独线程中运行
write, flush = sys.stdout.write, sys.stdout.flush
for char in itertools.cycle('|/-\\'): # |/—\|
status = char + ' ' + msg
write(status) # 写入到标准输出中,即可以在cmd里显示。
flush() # 刷新缓存,使其马上显示到屏幕上。
write('\x08'*len(status)) # \x08 退格符和\b等效。
# if done.wait(0.1): # 阻塞,直到done事件被设置为True 或 超时(这里设置为只阻塞0.1秒,但此时的done还是False)。
done.wait(0.1) # 阻塞线程,直到done事件被设置为True 或 超时(这里设置为只阻塞0.1秒,但此时的done还是False)。
if done.isSet(): # isSet()判断是 Event 是否为 True。
break
#write(' '*len(status) ) #+ '\x08'*len(status)
def slow_function(): # 假设这是 处理 I/O事件的耗时计算。
time.sleep(5) # 释放GIL锁给子线程用。
return 2222
def supervisor(): # 监督线程
done = threading.Event()
# event对象维护一个内部标志(初始为False),通过set()将其设置为True,
# wait(timeout) 用于堵塞线程直至Flag被set(可选的),clear()用于清除标志(使之为False)。
spinner = threading.Thread(target=spin, args=('thinking', done))
print(f'spinner object: {spinner}')
spinner.start()
result = slow_function() # 在主线程运行 I/O 计算。
done.set() # 设置done事件为True。
spinner.join()
return result
def main():
result = supervisor()
print(f'Answer: {result}', flush=True)
if __name__ == '__main__'
main()