import time
import threading
import random
import sys
event = threading.Event() # 创建事件线程开关
command_list = ['all', [0, 0, 0]] # 创建动作指令,用于主线程随机改变动作指令时,第1个值表示影响的人物,第2个值是列表,设置三个坐标方向的动作开关,与人物的移动状态相对应
lock = threading.RLock() # 创建可重用锁,用于当改变共用资源时,锁住线程
'''本例子用于演示在主线程中控制5个人物(子线程)的移动'''
class Actor: # 创建人物类
ids = {} # 静态字典用于存储所有已经创建的人物
pz_max = [100000, 100000, 100000]
def __init__(self, user_id, user_name, password):
if user_id in Actor.ids: # 如果id已经被创建过的人物所使用,则抛出异常,id键不能重复
raise Exception('id已被使用')
self.id = user_id
self.name = user_name
self.pw = password
self.age = None
self.sex = None
self.trans_state = [0, 0, 0] # 人物的移动状态,分别对x,y,z三个轴坐标设置了0,1,2状态码,分别表示:停止,正向,反向
self.step = 1 # 初始的移动步长都为1
self.current_tool = None # 人物的坐骑,初始无坐骑
self.pz = [50000, 50000, 50000] # 人物的初始坐标x,y,z
Actor.ids[self.id] = self # 一旦创建了一个人物,则将其加入静态字典中,其id为键,人物对象为值
@classmethod
def get_actor_from_id(cls, actor_id): # 静态方法,通过id获取人物对象
if actor_id in cls.ids:
return cls.ids[actor_id]
else:
return None
def ride(self, obj): # 人物的骑乘动作,obj是宠物实例
self.current_tool = obj # 设置本人物当前骑乘的宠物
self.step = obj.step # 将本人物的移动速度切换为宠物的移动速度
obj.owner = self # 将宠物的主人更改为本人物
def move_me(self):
while True:
for k in range(0, 3): # 循环从三个坐标方向读取状态
if self.trans_state[k] == 0: # 如果是停止状态
pass
elif self.trans_state[k] == 1: # 如果正向移动状态
if self.pz[k] < (Actor.pz_max[k] - self.step): # 判断移动后是否撞界
self.pz[k] += self.step
else:
self.pz[k] = Actor.pz_max[k]
else: # 如果是反向移动状态
if self.pz[k] > self.step: # 判断移动后是否撞界
self.pz[k] -= self.step
else:
self.pz[k] = 0
sys.stdout.write(f'{time.ctime()}: {self.name}状态:{self.trans_state} 坐标:{self.pz}\n')
time.sleep(0.25)
class TransTool: # 宠物类
def __init__(self, name, step, owner=None): # owner=None表示初始创建的宠物,无归属
self.name = name
self.step = step
self.owner = owner
class Action(threading.Thread): # 动作线程
def __init__(self, name, act): # act是人物实例
threading.Thread.__init__(self, name=name)
self.actor = act
def run(self): # 重写线程的run方法
global command_list # 动作指令设为全局变量
t = threading.Thread(target=self.actor.move_me) # 启动一个子线程,执行人物的移动方法
t.setDaemon(True) # 设置守护,可以跟随主线程的结束而结束,守护必要要在启动之前设置
t.start() # 启动移动的子线程
while True:
event.wait() # 等待接收到新的动作指令
if command_list[0] == 'all' or command_list[0] == self.name: # 判断新的动作指令是否与本线程相关
lock.acquire() # 相关时,则获取线程锁,更改本线程人物的移动状态
self.actor.trans_state = command_list[1]
lock.release() # 更改后随即释放线程锁
event.clear() # clear方法使事件线程重新阻塞
if __name__ == '__main__':
actors = [] # 人物列表
threads = [] # 子线程列表
for i in range(1, 6): # 循环创建5个人物
actors.append(Actor('A' + str(i), 'actor' + str(i), 123))
for i in range(1, 6): # 循环给5个人物配备坐骑,坐骑的移动速度以2的倍数递增
actors[i - 1].ride(TransTool('tool' + str(i), 2 ** (i - 1)))
for i in range(1, 6): # 循环给5个人物创建子线程
threads.append(Action('Thread' + str(i), act=actors[i - 1]))
for t in threads: # 循环给5个子线程设置守护,并启动
t.setDaemon(True) # 守护必须在子线程启动前设置
t.start() # 启动子线程
while True:
lock.acquire() # 改变动作指令前,先获取线程锁
command_list = [['all', 'Thread1', 'Thread2', 'Thread3', 'Thread4', 'Thread5'][random.randint(0, 5)], [random.randint(0, 2), random.randint(0, 2), random.randint(0, 2)]]
lock.release() # 更改动作指令后,立即释放线程锁
sys.stdout.write(f'新动作指令:{command_list}\n')
event.set() # 释放事件线程,触发子线程的wait后的程序代码执行
time.sleep(1) # IO时间阻塞