感知项目需要在sort中夹杂其他任务,跟踪结点的消息发送频率在15Hz以上,如果采用单线程的话会大大拖累sort的速度,因此采用 threading 实现双线程,本贴用一个例子来简述该过程。直接上代码:
import rospy
import threading
import time
'''
job()是子任务,
在主任务中一直循环检测,
当子任务的任务队列有任务时就执行
'''
def job():
while True:
if len(tasks)>0:
# 线程锁加锁
lock1.acquire()
# 修改共享变量
id = tasks.pop(0)
# 解除线程锁
lock1.release()
print(50*'-')
print(id)
print(tasks)
# 休眠1s,用来模拟子任务的计算耗时
time.sleep(1)
# 休眠,防止一直循环占用计算资源,也可不加
time.sleep(0.02)
def main():
# 启动结点,模拟sort跟踪结点
rospy.init_node('init', anonymous=True)
rate = rospy.Rate(15)
# lock是线程锁,防止一个线程在使用共享变量时被另一个线程更改
global lock1
lock1 = threading.Lock()
# 另一个线程,用来做子任务
thread1 = threading.Thread(target = job)
# tasks用来表示子任务队列,任务可以是车牌识别等等
global tasks
tasks = []
# k没啥用,只是控制一下循环次数,防止打印太多
k = 0
while k<4:
# 主任务
'''
主任务模拟sort.update,
当满足要求时,
将其添加到任务队列
'''
for i in range(21):
if i%5==0:
# 线程锁加锁
lock1.acquire()
# 修改共享变量
tasks.append(i)
# 解除线程锁
lock1.release()
if k<=0:
# 只启动一次,实际使用过程中需要设计一下启动条件
thread1.start()
# 打印结果
rospy.loginfo(tasks)
k += 1
rate.sleep()
if __name__ == '__main__':
main()
打印结果如下:
zxc@zxc-MSI:~/catkin_ws$ rosrun plumbing_server_client test.py
--------------------------------------------------
[INFO] [1664782453.415831]: [5, 10, 15, 20]
0
[5, 10, 15, 20]
[INFO] [1664782453.482819]: [5, 10, 15, 20, 0, 5, 10, 15, 20]
[INFO] [1664782453.549604]: [5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
[INFO] [1664782453.616296]: [5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
5
[10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
10
[15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
15
[20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
20
[0, 5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
0
[5, 10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
5
[10, 15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
10
[15, 20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
15
[20, 0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
20
[0, 5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
0
[5, 10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
5
[10, 15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
10
[15, 20, 0, 5, 10, 15, 20]
--------------------------------------------------
15
[20, 0, 5, 10, 15, 20]
--------------------------------------------------
20
[0, 5, 10, 15, 20]
--------------------------------------------------
0
[5, 10, 15, 20]
--------------------------------------------------
5
[10, 15, 20]
--------------------------------------------------
10
[15, 20]
--------------------------------------------------
15
[20]
--------------------------------------------------
20
[]
主任务是从0-20遍历,当i是5的倍数时加入任务队列,此时子任务检测到任务队列并行执行任务。从结果来看,由于主任务的频率更高,因此很快执行完,子任务在之后会继续执行。
线程锁是为了防止各个线程同时修改内存,使得结果发生异常,因此在对共享变量进行操作时需要加锁,此时有且只有一个加锁的线程有权对内存进行修改,其他线程都被阻塞。
简单地说,各线程除了在线程锁加锁时是串行执行,其他时间都是并行执行。
参考
多线程解决rospy.spin()语句之后,程序不再往下执行问题
Python Lock acquire()实例讲解