带递归的多线程Python乌龟
我制作了一个递归生成分形树的Python乌龟程序,但由于它经常需要几个小时才能完全绘制,我想尝试使用多线程来让多个乌龟一起工作。
我能够同时获得两只乌龟,但在这种更加复杂的情况下,一切似乎都崩溃了。 我尝试了许多不同的方式,并认为最终的解决方案将是唯一的解决方案,但它只是抛出了一堆错误。
这是我的代码:
import turtle
import threading
from queue import Queue
class Location:
def __init__(self, xpos=0, ypos=0, heading=90):
self.xpos = xpos
self.ypos = ypos
self.heading = heading
def getx(self):
return self.xpos
def gety(self):
return self.ypos
def geth(self):
return self.heading
class Turtle(turtle.Turtle):
def tolocation(self, location):
self.penup()
self.setx(location.getx())
self.sety(location.gety())
self.setheading(location.geth())
self.pendown()
def get_location(self):
return Location(self.xcor(), self.ycor(), self.heading())
def draw_tree(self, startpos=Location(), size=100):
tm.q.put(self.tolocation(startpos))
for _ in range(size):
tm.q.put(self.forward(1))
for _ in range(45):
tm.q.put(self.right(1))
t2 = Turtle()
t2.speed(0)
tm.new_thread(t2.draw_tree, self.get_location(), size / 2)
for _ in range(90):
tm.q.put(self.left(1))
tm.new_thread(self.draw_tree, self.get_location(), size / 2)
class ThreadManager:
def __init__(self):
self.q = Queue()
self.threads = []
def new_thread(self, func, *args):
self.threads.append(threading.Thread(target=func, args=(args,)))
self.threads[-1].daemon = True
self.threads[-1].start()
def process_queue(self, scr):
while not self.q.empty():
(self.q.get())(1)
if threading.active_count() > 1:
scr.ontimer(self.process_queue(scr), 100)
tm = ThreadManager()
scr = turtle.Screen()
t1 = Turtle()
t1.speed(0)
tm.new_thread(t1.draw_tree)
tm.process_queue(scr)
scr.exitonclick()
任何人都可以给我一个我在哪里出错的想法吗? 当process_queue自己调用时,错误消息会说递归的线条太深。 我使用scr.ontimer()
错误吗?
你的代码有几个问题:
draw_tree()
没有基本的情况来阻止它的(概念)递归,它只是不断创建新的线程。
每次调用draw_tree()
使用浮动除法将size
分成两半,因此range(size)
将失败,因为range()
只能取整数。
您对self.get_location()
调用无效,因为它会告诉您乌龟在哪里,而不是在主线程完成处理未完成的图形命令后它将在何处。 你必须计算你的位置,而不是看你在哪里。
这个调用tm.q.put(self.tolocation(startpos))
是无效的 - 你可能需要对tm.q.put(self.tolocation, startpos)
执行tm.q.put(self.tolocation, startpos)
或调用tm.q.put()
self.tolocation()
命令。
你不能在主线程的任何地方创建新的乌龟,因为它们在创建时调用tkinter,并且会在错误的(不是主线程)线程上。 在我下面的返工中,我只是预先分配他们。
args=(args,)
不正确 - 应该是args=args
因为args
已经处于正确的格式。
您不需要在每个分支点创建两个新线程,只需一个。 新的乌龟走一条路,老乌龟继续另一条路。
以下是我对代码的返工,以解决上述问题和其他问题:
import math
import threading
from queue import Queue
from turtle import Turtle, Screen
class Location:
def __init__(self, xpos=0, ypos=0, heading=90):
self.xpos = xpos
self.ypos = ypos
self.heading = heading
def clone(self):
return Location(self.xpos, self.ypos, self.heading)
class Terrapin(Turtle):
def tolocation(self, location):
tm.q.put((self.penup,))
tm.q.put((self.setx, location.xpos))
tm.q.put((self.sety, location.ypos))
tm.q.put((self.setheading, location.heading))
tm.q.put((self.pendown,))
def draw_tree(self, startpos, size=100):
if size < 1:
return
self.tolocation(startpos)
tm.q.put((self.forward, size))
angle = math.radians(startpos.heading)
startpos.xpos += size * math.cos(angle)
startpos.ypos += size * math.sin(angle)
tm.q.put((self.right, 45))
startpos.heading -= 45
tm.new_thread(pond.get().draw_tree, startpos.clone(), size / 2)
tm.q.put((self.left, 90))
startpos.heading += 90
self.draw_tree(startpos, size / 2)
pond.put(self) # finished with this turtle, return it to pond
class ThreadManager:
def __init__(self):
self.q = Queue()
self.threads = Queue()
def new_thread(self, method, *arguments):
thread = threading.Thread(target=method, args=arguments)
thread.daemon = True
thread.start()
self.threads.put(thread)
def process_queue(self):
while not self.q.empty():
command, *arguments = self.q.get()
command(*arguments)
if threading.active_count() > 1:
screen.ontimer(self.process_queue, 100)
screen = Screen()
# Allocate all the turtles we'll need ahead as turtle creation inside
# threads calls into Tk which fails if not running in the main thread
pond = Queue()
for _ in range(100):
turtle = Terrapin(visible=False)
turtle.speed('fastest')
pond.put(turtle)
tm = ThreadManager()
tm.new_thread(pond.get().draw_tree, Location())
tm.process_queue()
screen.exitonclick()
如果不是大步骤:
tm.q.put((self.right, 45))
您想要将图形命令分解为几个小步骤:
for _ in range(45):
tm.q.put((self.right, 1))
没关系,我只是想让代码运行。 你需要弄清楚这是否会给你带来任何好处。
链接地址: http://www.djcxy.com/p/80715.html