使用Python Turtle进行多线程处理

5

有没有办法在同一个窗口中同时使用两只海龟画出两个圆?我尝试了这段代码,但是两只海龟分别在不同的窗口中画圆。

from multiprocessing import Process
import turtle

t1=turtle.Turtle()
t2=turtle.Turtle()

def tes1():
  t1.speed(0)
  i=0
  while i < 360:
    t1.forward(1)
    t1.left(1)
    i+=1

def tes2():
  t2.speed(0)
  i=0
  while i < 360:
    t2.forward(1)
    t2.right(1)
    i+=1

if __name__ == '__main__':
  p1 = Process(target=tes1)
  p1.start()
  p2 = Process(target=tes2)
  p2.start()
  p1.join()
  p2.join()

但有人告诉我要尝试使用多线程,但是这段代码存在严重语义错误!
import threading
import turtle

t1=turtle.Turtle()
t2=turtle.Turtle()

def tes1():
  t1.speed(0)
  i=0
  while i < 360:
    t1.forward(1)
    t1.left(1)
    i+=1

def tes2():
  t2.speed(0)
  i=0
  while i < 360:
    t2.forward(1)
    t2.right(1)
    i+=1

t = threading.Thread(target=tes1)
t.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
t.start()

t3 = threading.Thread(target=tes2)
t3.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
t3.start()

那么最好的建议是使用多进程还是多线程?

7个回答

8

......我希望得到多线程或多进程的解决方案,我会坚持这个要求。

如果我们小心地在只有主线程发出海龟命令的情况下使用海龟模块,则可以将其与线程一起使用:

import queue
import threading
import turtle

def tes1():
    for _ in range(360):
        graphics.put(turtle1.forward)
        graphics.put(turtle1.left)

def tes2():
    for _ in range(360):
        graphics.put(turtle2.forward)
        graphics.put(turtle2.right)

def process_queue():
    while not graphics.empty():
        (graphics.get())(1)

    if threading.active_count() > 1:
        turtle.ontimer(process_queue, 100)

graphics = queue.Queue(1)  # size = number of hardware threads you have - 1

turtle1 = turtle.Turtle('turtle')
turtle1.speed('fastest')
thread1 = threading.Thread(target=tes1)
thread1.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
thread1.start()

turtle2 = turtle.Turtle('turtle')
turtle2.speed('fastest')
thread2 = threading.Thread(target=tes2)
thread2.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
thread2.start()

process_queue()

turtle.exitonclick()

我们正在使用队列模块进行线程安全通信。

enter image description here


3

需要让乌龟在不同的线程中吗?这个怎么样呢?

import turtle

t1 = turtle.Turtle()
t2 = turtle.Turtle()

t1.speed(0)
t2.speed(0)
for i in range(360):
  t1.forward(1)
  t1.left(1)
  t2.forward(1)
  t2.right(1)

1
你的答案对于两只海龟来说很好,但是想象一下我们有8只海龟,我们想让它们同时绘制,这样处理速度就非常慢了! - hamidfzm
1
也许mtTkinter可以帮助你。如果你试图制作超级高效的海龟,我认为你走错了方向。 - John La Rooy
3
我认为那是一个不好的解决方案,因为你无法像cdlane提供的解决方案一样分别管理每只海龟的绘制。 - projetmbc

2
我创建了一个名为threaded_turtle的包,利用了queue.Queue的功能,在主线程中无缝执行所有海龟指令,而代码仍然像不同线程中的海龟一样编写。 threaded_turtle在GitLab上:https://gitlab.com/zvone/threaded_turtle 使用该软件包后,问题中的代码仅需要进行微小修改即可运行:
import turtle
from threaded_turtle import ThreadSerializer, TurtleThread

ctrl = ThreadSerializer()                        ## <-- create a serializer

t1=turtle.Turtle()
t2=turtle.Turtle()

def tes1(t1):                                    ## <-- additional argument
  t1.speed(0)
  i=0
  while i < 360:
    t1.forward(1)
    t1.left(1)
    i+=1

def tes2(t2):                                    ## <-- additional argument
  t2.speed(0)
  i=0
  while i < 360:
    t2.forward(1)
    t2.right(1)
    i+=1

t = TurtleThread(ctrl, t1, target=tes1)          ## <-- additional arguments
t.daemon = True
t.start()

t3 = TurtleThread(ctrl, t2, target=tes2)         ## <-- additional arguments
t3.daemon = True
t3.start()

ctrl.run_forever(1)                              ## <-- run the serializer

结果:

Screenshot of two turtles circling simultaneously


当我访问你的代码库时,我没有看到setup.py文件,那么你是如何安装你的模块的呢? - yemi.JUMP
@yemi.JUMP 只需将 threaded_turtle 包(即目录)放在您的 pythonpath 中的某个位置,例如靠近您的源代码。 - zvone

1
每次8只海龟也不是问题。
import turtle
turtle.delay(0)

t = [turtle.Turtle() for i in range(8)]

for i, j in enumerate(t):
    j.right(i*45)
    j.speed(0)

for i in range(360):
    for j in t:
        j.forward(1)
        j.right(1)

1
这是真的,但请给我一个多线程或多进程的答案,我坚持要这样。 - hamidfzm

1

乌龟模块不支持多线程。我认为你可以做的唯一事情就是像其他人已经建议的那样,制作一堆乌龟。或者,你可以使用类似于mtTkinter这样的东西,它与tkinter完全相同,但支持线程。


1
我认为Beazley在第447页宣扬的协程和生成器在这里更加合理:
注意:来自collections模块的deque也更加可靠。
import turtle
from collections import deque


def move1():
    for _ in range(360):
        turtle1.forward(1)
        turtle1.left(1)
        yield


def move2():
    for _ in range(360):
        turtle2.forward(1)
        turtle2.right(1)
        yield


# Create turtles
turtle1 = turtle.Turtle('turtle')
turtle1.speed('fastest')
turtle2 = turtle.Turtle('turtle')
turtle2.speed('fastest')

# Create and populate a task queue

taskqueue = deque()
taskqueue.append(move1())  # Add tasks (generators)
taskqueue.append(move2())

while taskqueue:   # Run all of the tasks
    # Get the next task
    task = taskqueue.pop()
    try:
        # Run it to the next yield and enqueue
        next(task)
        taskqueue.appendleft(task)
    except StopIteration:
        # Task is done
        pass

turtle.done()

0
没有一个答案真正加快了绘图速度,为了证明这一点,我进行了一项测试来比较它们:
import turtle
from time import time
import queue
import threading

start = time()

turtle.speed(0)
for i in range(360):
    turtle.forward(1)
    turtle.left(1)

for i in range(360):
    turtle.forward(1)
    turtle.right(1)

result1 = time()-start
start = time()


def tes1():
    for i in range(360):
        graphics.put(turtle1.forward)
        graphics.put(turtle1.left)

def tes2():
    for i in range(360):
        graphics.put(turtle2.forward)
        graphics.put(turtle2.right)

graphics = queue.Queue(1)  # size = number of hardware threads you have - 1

turtle1 = turtle.Turtle('turtle')
turtle1.speed(0)
thread1 = threading.Thread(target=tes1)
thread1.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
thread1.start()

turtle2 = turtle.Turtle('turtle')
turtle2.speed(0)
thread2 = threading.Thread(target=tes2)
thread2.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
thread2.start()

while not graphics.empty():
    (graphics.get())(1)

resutl2 = time()-start

print(f"time to draw without multithreading: {result1}\n\
      time with multithreading: {resutl2}")

而结果是多线程实际上更慢,在我的机器上我得到了以下结果:
没有多线程绘制的时间:16.973097562789917
多线程绘制的时间:17.62730455398559
我认为多线程较慢的原因是我们只对寻找路径的过程进行了多线程处理,而没有对绘制过程进行多线程处理。然后我们在单个线程上按顺序执行方向,跳转到绘制的乌龟,使其看起来像它们同时绘制。计算路径可能是多线程处理中耗时较长的部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接