使用线程中断Python循环

bnlyeluc  于 2023-09-29  发布在  Python
关注(0)|答案(2)|浏览(143)

我希望有一个线程不断地监视文本文件中的值的状态,另一个线程在循环中执行一些代码,我希望当文本文件中的值改变时,立即中断循环并执行else语句,而不管程序在循环中的哪个部分。有可能做到这一点吗?
这是我的Python 3代码:

import threading
import time

text_list = ['a']

def readfile():
    global text_list
    while True:
        with open("data.txt") as f: 
            text_list = f.readlines()

        # Removing new line "\n" character
        text_list = [x.strip() for x in text_list]
         
def printloop():
    while str(text_list[0]) == 'a':    
        for n in range(0,5):
            print('File Character: '+str(text_list[0])+', Iteration: '+str(n))
            time.sleep(5)
    else:
        print('File Character: '+str(text_list[0])+', Loop Broken')
        

t1 = threading.Thread(target=readfile, daemon=True)
t2 = threading.Thread(target=printloop)

t1.start()
t2.start()

这是data.txt的内容

a

问题是,当我执行代码时,我必须等待time.sleep函数完成。我想在变量改变时中断循环。我认为一个解决方案可能是告诉time.sleep函数等待更少的时间并增加迭代次数,或者使用类似Arduino中的米尔斯()函数的东西,但我认为最好保持time.sleep函数不变,因为我的文件中的真实的循环要大得多,我使用了不止一个time.sleep函数,所以如果变量发生变化,无论它在哪个部分,是否有办法打破循环?

eyh26e7m

eyh26e7m1#

首先,如果输入文件没有更改,您可能不希望无效率地连续重新读取它。根据DarkNight的建议,您可能希望查看PyPi存储库上的watchdog模块。在这里,我们将只查看文件的初始修改时间,并且只在它发生更改时重新读取它(我们将每隔0.1秒检查修改时间)。其次,在原始代码中,如果另一个进程正在打开文件,则在执行任何写入之前,其长度将为0。因此,您有一个竞争条件,其中readfile线程可能正在阅读这个长度为0的文件并相应地设置text_list。这可能导致表达式text_list[0]引发异常。在这里,由于我们只在修改时间发生变化时阅读文件,这似乎是 * 在 * 新数据写入和文件关闭之后,除非有两个这样的文件重写一个接一个地执行,否则这不是问题。但为了安全起见,我们仍将检查临时的0长度文件内容。
您的问题的解决方案是使用一个threading.Condition示例,该示例表示发生了文件内容更改。线程printloop将等待最多5秒,以使此to事件发生。如果5秒过去了或者文件发生了更改,线程将被唤醒并执行其测试。在原始代码中,即使文件已经更改,它仍然会在终止之前循环多达5次打印消息。我已经修改了代码,这样就不会发生这种情况。
我将函数readfile重命名为monitor_file,并创建了一个read_file函数,monitor_file使用该函数重新读取文件内容并返回结果。

import threading
import time
import os

file_changed = threading.Condition()

PATH = 'data.txt'

def read_file():
    with open(PATH) as f:
        # Removing new line "\n" character in each line:
        return [line.strip() for line in f]

# Get actual initial modification time and file contents.
# Note the order in which these two operations occur in case
# the file is modified immediately after we have done this initial read:
modification_time = os.stat(PATH)
text_list = read_file()

def monitor_file():
    global modifcation_time, text_list

    while True:
        time.sleep(.1)
        new_modification_time = os.stat(PATH)
        if new_modification_time == modification_time:
            # The file has not changed:
            continue

        modifcation_time = new_modification_time
        new_text_list = read_file()
        # Make sure not 0 length:
        if new_text_list:
            text_list = new_text_list
            if text_list[0] != 'a':
                # Tell other thread that the file has changed:
                with file_changed:
                    file_changed.notify()

def printloop():
    n = 0
    # text_list is already a list of strings
    while text_list[0] == 'a':
        # Use an f-string:
        print(f'File Character: {text_list[0]}, Iteration: {n}')
        # Wait for up to 5 seconds to see if the file has
        # changed:
        with file_changed:
            file_changed.wait(5)
            n += 1
    else:
        print(f'File Character: {text_list[0]}, Loop Broken')

t1 = threading.Thread(target=monitor_file, daemon=True)
t2 = threading.Thread(target=printloop)

t1.start()
t2.start()
# Wait for printloop to end:
t2.join()
e37o9pze

e37o9pze2#

你的程序可以通过使用看门狗简化。

#!/usr/bin/env python3

from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

file_name = 'data.txt'
file_path = Path(__file__).with_name('data.txt').resolve()
dir_path = file_path.parent

class FileWatch(FileSystemEventHandler):
    def on_modified(self, event):
        src_path = event.src_path
        src = Path(src_path).relative_to(dir_path).__str__()
        if src != file_name:
            print(f'{src!r} changed but it is not {file_name!r}')
            return
        text = file_path.read_text().strip()
        print('File Character:', text)
        if text == 'a':
            print('Not changed')
            return
        print('Changed; stop the observer')
        observer.stop()

observer = Observer()
observer.schedule(FileWatch(), path=dir_path, recursive=False)
observer.start()
observer.join()

"""
In Python, f-strings, also known as "formatted string literals,"
are a way to embed expressions inside string literals, using curly braces {}.
The expressions inside the curly braces are evaluated at runtime and then
formatted and substituted into the resulting string using the format specified.

In the given f-string f'{src!r} changed but it is not {file_name!r}',
src and file_name are variables or expressions whose values will be inserted
into the string.
The !r after the variable name inside the curly braces is a format specifier,
which means to use the repr() representation of the variable.
The repr() representation is a string representation of the object that,
when passed to eval(), would produce an object equivalent to the original.
In other words, it’s a string representation that includes quotes around
string objects.

So, this f-string is saying: "Evaluate src and file_name, convert them to
their repr() representation (add quotes around if they are strings),
and then insert them into the resulting string at their respective places."
"""

您可能需要安装watchdog。例如,您可以通过运行python -m pip install watchdog来执行此操作。

相关问题