(Python) 如何通过函数传递 threading.Thread 对象?

0 投票
2 回答
1674 浏览
提问于 2025-04-15 14:50

我有一个定时器功能:

# This is a class that schedules tasks. It will call it's ring() function
# when the timer starts, and call it's running() function when within the
# time limit, and call it's over() function when the time is up.

# This class uses SYSTEM time.

import time, threading
import settings

from object import Object

class Timer(Object, threading.Thread):
    # INIT -------------------------------------------------------------
    # Init vars
    #
    # If autotick is True (default) the timer will run in a seperate
    # process. Other wise it will need to be updated automatically by
    # calling tick()
    def __init__(self, autotick=False):
        # Call inherited __init__ first.
        threading.Thread.__init__(self)
        Object.__init__(self)

        # Now our vars
        self.startTimeString = "" # The time when the timer starts as a string
        self.endTimeString = "" # The time when the timer stops as a string
        self.timeFormat = "" # The string to use as the format for the string       
        self.set = False # The timer starts deactivated
        self.process = autotick # Wether or not to run in a seperate process.
        self.rung = False # Has the timer rang yet?


    # ACTIVATE --------------------------------------------------------------
    # Sets the timer
    def activate(self, startTime, endTime, format):
        # Set the timer.
        self.startTimeString = startTime
        self.endTimeString = endTime
        self.timeFormat = format

        # Conver the strings to time using format
        try:
            self.startTime = time.strptime(startTime, self.timeFormat)
            self.endTime = time.strptime(endTime, self.timeFormat)
        except ValueError:
            # Error
            print ("Error: Cannot convert time according to format")
            return False

        # Try and convert the time to seconds
        try:
            self.startTimeSecs = time.mktime(self.startTime)
            self.endTimeSecs = time.mktime(self.endTime)
        except OverflowError, ValueError:
            # Error
            print ("Error: Cannot convert time to seconds")
            return False

        # The timer is now set
        self.set = True

        # If self.process is true, we need to start calling tick in a
        # seperate process.
        if self.process:
            self.deamon = True # We don't want python to hang if a timer
                                # is still running at exit.
            self.start()

    # RING -------------------------------------------------------------
    # This function is called when the timer starts.
    def ring(self):
        pass

    # RUNNING ----------------------------------------------------------
    # Called when the the time is whithin the time limits.
    def running(self):
        pass

    # OVER -------------------------------------------------------------
    # Called when the time is up
    def over(self):
        pass

    # TICK -------------------------------------------------------------
    # Call this every loop (or in a seperate process)
    def tick(self): 
        print time.time(), self.startTimeSecs, self.endTimeSecs, time.strftime("%A %H:%M", time.localtime(self.startTimeSecs))

        # Check the time
        if time.mktime(time.localtime()) > self.startTimeSecs and time.mktime(time.localtime()) < self.endTimeSecs and not self.rung:
            # The time has come =)
            # Call ring()
            self.ring()

            # Now set self.rung to True
            self.rung = True

        # If the time is up..
        elif time.mktime(time.localtime()) > self.endTimeSecs and self.rung:
            self.over()

            # Unset the timer
            self.set = False
            self.rung = False

        # If we are inbetween the starttime and endtime.
        elif time.mktime(time.localtime()) > self.startTimeSecs and time.mktime(time.localtime()) < self.endTimeSecs and self.rung:
            self.running()

        # If any of those aren't true, then the timer hasn't started yet
        else:
            # Check if the endTime has already passed
            if time.mktime(time.localtime()) > self.endTimeSecs:

                # The time has already passed.
                self.set = False


    # THREADING STUFF --------------------------------------------------
    # This is run by Threads start() method.
    def run(self):
        while self.set == True:
            # Tick
            self.tick()

            # Sleep for a bit to save CPU
            time.sleep(settings.TIMER_SLEEP)

我还在一个调度器里添加了时间块:

# LOAD -------------------------------------------------------------
# Loads schedule from a file (schedule_settings.py).
def load(self):
    # Add blocks
    for block in schedule_settings.BLOCKS:
        # Calculate the day
        start_day = str(getDate(block[1].split()[0]))
        end_day = str(getDate(block[2].split()[0]))

        self.scheduler.add(start_day + " " + block[1].split()[1], end_day + " " + block[2].split()[1], "%j %H:%M", block[0])

    for block in self.scheduler.blocks:
        block.timer.tick()

    print len(self.scheduler.blocks)

    # Start the scheduler (if it isn't already)
    if not self.scheduler.running:
        self.scheduler.start()

添加功能看起来是这样的:

# ADD --------------------------------------------------------------
# Add a scheduled time
# 
# block should be a Block instance, describing what to do at this time.
def add(self, startTime, endTime, format, block):
    # Add this block
    newBlock = block

    # Start a timer for this block
    newBlock.timer = Timer()

    # Figure out the time
    year = time.strftime("%Y")

    # Add the block timer
    newBlock.timer.activate(year + " " + startTime, year + " " + endTime, "%Y " + format)

    # Add this block to the list
    self.blocks.append(newBlock)

    return

简单来说,我的程序可以让你制定一周的时间表,并像电视台一样播放视频。一个时间块就是一段时间,在这段时间里,频道会播放特定的剧集或系列。

我的问题是,使用添加功能后,这些时间块完全乱了。有些块被重复了,有些顺序错了等等。不过在使用添加功能之前,它们是完全正常的。如果我只用少量的时间块(2或3个),似乎就没问题。

举个例子,如果我的 schedule_settings.py(设置一周的时间表)看起来是这样的:

# -*- coding: utf-8 -*-
# This file contains settings for a week's schedule
from block import Block
from series import Series

# MAIN BLOCK (All old episodes)
mainBlock = Block()
mainBlock.picker = 'random'
mainBlock.name = "Main Block"
mainBlock.auto(".")
mainBlock.old_episodes = True

# ONE PIECE
onepieceBlock = Block()
onepieceBlock.picker = 'latest'
onepieceBlock.name = "One Piece"
onepieceBlock.series = [
        Series(auto="One Piece"),
]

# NEWISH STUFF
newishBlock = Block()
newishBlock.picker = 'random'
newishBlock.auto(".")
newishBlock.name = "NewishBlock"
newishBlock.exclude_series = [
        #Series(auto="One Piece"),
        #Series(auto="Nyan Koi!"),
]

# NEW STUFF
newBlock = Block()
newBlock.picker = 'latest'
newBlock.name = "New Stuff"
newBlock.series = [
        Series(auto="Nyan Koi!"),
]

# ACTIVE BLOCKS
BLOCKS = (
        # MONDAY
        (mainBlock, "Monday 08:00", "Monday 22:20"),
        (onepieceBlock, "Monday 22:20", "Monday 22:30"),
        (newishBlock, "Monday 22:30", "Monday 23:00"),

        # TUESDAY
        (mainBlock, "Tuesday 08:00", "Tuesday 18:00"),
        (newBlock, "Tuesday 18:00", "Tuesday 18:30"),
        (newishBlock, "Tuesday 18:30", "Tuesday 22:00"),

        # WEDNESDAY
        (mainBlock, "Wednesday 08:00", "Wednesday 18:00"),
        (newBlock, "Wednesday 18:00", "Wednesday 18:30"),
        (newishBlock, "Wednesday 18:30", "Wednesday 22:00"),

        # THURSDAY
        (mainBlock, "Thursday 08:00", "Thursday 18:00"),
        (newBlock, "Thursday 18:00", "Thursday 18:30"),
        (newishBlock, "Thursday 18:30", "Thursday 22:00"),

        # FRIDAY
        (mainBlock, "Friday 08:00", "Friday 18:00"),
        (newBlock, "Friday 18:00", "Friday 18:30"),

        # WEEKEND
        (newishBlock, "Saturday 08:00", "Saturday 23:00"),

        (newishBlock, "Sunday 08:00", "Sunday 23:00"),

)

在添加到调度器之前,生成的列表看起来很好,但在添加后再打印出来,我得到的是:

1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1254777600.0 1254778200.0 Monday 22:20
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00

我猜这可能和我在 Timer 类中对 threading.Thread 的子类化有关。把它通过一个函数传递并添加到列表中,会不会以某种方式搞乱了呢?

(编辑)抱歉,如果我说得不够清楚,我有点赶,忘了发最重要的代码 =( 用定时器手动滴答只是一些调试代码,正常情况下我会在定时器类中设置 auto=True。

你可以在这里找到我的所有代码:http://github.com/bombpersons/MYOT

2 个回答

0

唉,我现在觉得自己挺傻的。问题在于,块(block)是通过引用传递给调度器的,所以每次我在块里添加一个定时器的时候,实际上是在覆盖之前的定时器。

我创建了一个叫做schedulerBlock的类,里面包含了一个定时器和一个块。现在一切都正常运作了 =)

1

你给我们展示了很多代码,但关键部分没有,包括调度器的部分(还有那个奇怪的大写字母O的Object类是什么?)。不过,回答你的问题,传递线程子类的实例到函数里,或者把它们添加到列表中等等,这些都是完全可以的(虽然你做的其他事情可能就不太对了——比如,你可能不知道,虽然tick是线程子类的方法,但从另一个线程调用它并不意味着它会在自己的线程中执行……实际上,它会在调用它的那个线程中执行)。

我建议你使用Python标准库中的sched模块来实现调度功能,而不是自己去写一个……

撰写回答