【问题标题】:I dont understand python threading我不懂python线程
【发布时间】:2021-04-09 12:13:38
【问题描述】:

我尝试运行这段代码,其中两个线程应根据计划任务进行初始化。我只想让守护进程并行,在一定时间后打印出一个非特定的全局值。

import os
import schedule
import time
import threading
from threading import Thread

myval = 0
thrs = []

def clear():
    os.system('cls' if os.name=='nt' else 'clear')

def loopfunc(t):
    global myval
    myval = t
    clear()
    print("myval=",t)

def primefunc(t):
    schedule.every(5).to(20).seconds.do(
        loopfunc,t)
    while True:
        schedule.run_pending()
        time.sleep(1)

def main():
    for num in [1,2]:
        thrs.append(Thread(target=primefunc(num)))
        thrs[-1].start()
    print(threading.active_count)

if __name__ == '__main__':
    main()

当尝试如上所述运行它时,只有第一个线程被初始化并循环运行。所以我的输出总是myval=1

当我为目标函数插入lambda 时:

thrs.append(Thread(target=lambda:primefunc(num)))

两个线程都已初始化,但我有随机双输出

myval=2
myval=2

myval=1
myval=1

这里发生了什么? 亲切的问候,MK

【问题讨论】:

  • 这一行thrs.append(Thread(target=primefunc(num))) 是错误的,您调用的是函数而不是将其作为参数传递。如果你想将num 作为参数传递给你需要做的函数thrs.append(Thread(target=primefunc, args=[num]))
  • 非常感谢,你说得对。但是这样做时,我仍然会随机获得双输出
  • 你怎么知道的?每次执行线程时,您都会使用 clear() 重置屏幕
  • 是的,这就是为什么它应该只打印一次“myval=X”,但有时它会显示相同值的双重打印输出。
  • 一般来说,这对于仅并行运行 2 个函数来说似乎有点矫枉过正,好的旧线程有什么问题?

标签: python python-3.x multithreading scheduled-tasks python-multithreading


【解决方案1】:

第一个问题是因为你在这里直接调用了primefunc函数,而不是把它作为参数传递给Thread。

thrs.append(Thread(target=primefunc(num)))

您需要将其包装在 lambda 中或提供裸函数名称作为目标,并在 Thread 的 args 关键字参数中提供 num 参数,如下所示:

thrs.append(Thread(target=lambda: primefunc(num)))

# or

thrs.append(Thread(target=primefunc, args=[num]))

对于您的输出加倍,我怀疑这里发生的情况是您有两个调度程序循环在不同的线程中同时运行,并且它们在从内部队列中删除之前都选择了相同的jobThe code for scheduler is on github, and you can see that the job runner does not perform any kind of locking around the running of tasks and removing them from the queue to make the two operations atomic together.

以下是可能导致该场景的一系列事件的示例。假设我们的执行时间排在第 7 位,调度程序决定添加loopfunc(1) 的执行。以下是之后可能发生的一系列事件:

  1. 线程 1 运行 schedule.run_pending()
  2. 线程 1 在调度程序队列中找到作业 loopfunc(1)
  3. 线程 1 运行 loopfunc(1) 并输出 myval=1
  4. 在从队列中删除 loopfunc(1) 之前,会发生到线程 2 的上下文切换。线程 2runs schedule.run_pending()
  5. 线程 2 在调度程序队列上找到作业 loopfunc(1)
  6. 线程 2 运行 loopfunc(1) 并输出 myval=1
  7. 线程 2 将其从队列中移除。
  8. 上下文切换发生回线程 1。
  9. 线程 1 尝试从队列中删除 loopfunc(1),但它不再存在。

还有其他几种可能发生的方式,并且不能保证每次都会发生,但您可以看看如何最终导致myval=1 被打印两次。

我认为这里的解决方案是放弃调度程序,因为它的目的与这里的线程相同。相反,也许只是生成随机数并自己在primefunc 中跟踪时间,例如:

def primefunc(t):
    time = 0
    r = random.randint(5, 20)
    while True:
        if time % r == 0:
            loopfunc(t)
            r = random.randint(5, 20)
        time.sleep(1)
        time += 1

您也可以选择相反的方法,取消线程并保留调度程序。然后你可以在你的 main 函数中运行调度程序循环,它看起来像这样:

def main():
    for num in [1,2]:
        schedule.every(5).to(20).seconds.do(loopfunc,num)
    while True:
        schedule.run_pending()
        time.sleep(1)

【讨论】:

  • 好的,非常感谢,我不知道线程之间的上下文切换。我认为线程以某种方式仅限于初始设置输入并且单独/独立工作。
  • 线程共享相同的内存,而多个进程运行在独立的内存上。因此,如果您希望任务独立运行,使用多处理也可能是比线程更快的方法。
  • 谢谢@aargun,它似乎只是通过使用多处理来按预期工作。但总的来说,也许 Rob 和 anti 尝试实现更苗条的解释是正确的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多