python中的奇怪行为

时间:2010-04-04 20:53:34

标签: python namespaces multithreading

标签可能不准确,因为我不确定问题出在哪里。

我有一个模块,我试图从套接字读取一些数据,并将结果写入文件(追加)它看起来像这样,(只包括相关部分)

if __name__ == "__main__":
    <some init code>
    for line in file:
        t = Thread(target=foo, args=(line,))
        t.start()
    while nThreads > 0: 
        time.sleep(1)

以下是其他模块,

def foo(text):
    global countLock, nThreads
    countLock.acquire()
    nThreads += 1
    countLock.release()

    """connect to socket, send data, read response"""
    writeResults(text, result)

    countLock.acquire()
    nThreads -= 1
    countLock.release()

def writeResults(text, result):
    """acquire file lock"""
    """append to file"""
    """release file lock"""

现在问题就在于此。最初,我在函数'foo'中输入了一个拼写错误,我将变量'line'传递给writeResults而不是'text'。 'line'没有在函数foo中定义,它在主块中定义,所以我应该看到一个错误,但相反,它工作正常,除了数据被多次附加到文件,而不是只是写入曾经,这是我修复错字时得到的所需行为。

我的问题是,

1)为什么我没有收到错误?

2)为什么多次调用writeResults函数?

3 个答案:

答案 0 :(得分:1)

你真的应该使用Thread.join()作为主循环来等待线程完成:

if __name__ == "__main__":
    <some init code>
    threads = []
    for line in file:
        t = Thread(target=foo, args=(line,))
        t.start()
        threads.append(t)
    for t in threads:
        t.join()

并删除nThreads全局和countLock

关于你的问题,我不知道为什么你没有得到错误(可能是异常被某些东西吃掉了?),我想知道writeResults的重复次数是否与数字有关文件中的行。如果确实如此,我将不得不怀疑line是否是全局的,并且每个线程都写了一次。

答案 1 :(得分:1)

当你有(简化到重要部分)时

def foo(text):
    writeResults(line, result)

foo,没有本地变量line,正在使用该名称的全局变量...恰好是一组(在主线程中) )for line in file:

具体来说,我希望写入的总行数是正常的:每行有一个线程(一个奇怪的架构,BTW),每个线程写一行...唯一的问题是,哪个每个线程写入行。

在你的意图中,第一个线程写第一行,第二个线程写第二行等;但实际上,在线程调用line的关键时刻,每个线程都会写出恰好绑定到全局writeResults名称的行。因此,有些行最终可能会多次写入,有些则不会写入。

例如,假设主线程运行得足够快,可以在任何实际写入之前启动所有子线程。在这种情况下,全局名称line采用的 last 值(即文件中的最后一行)将是所有线程写入的值。

请注意,即使在“更正”的版本中也无法保证 order ,其中各行都会被写入,这是使这种架构变得奇怪的一部分 - 通常,因为线路来了按照特定顺序,您希望在输出中保留该顺序。我猜你的应用案例很奇怪,不需要那个约束,但我仍然感到困惑的是,当你从一个文件读取并写入一个文件时,你需要这么多线程! - )

答案 2 :(得分:1)

为了避免名为line的全局变量,您应该编写一个名为main的函数来完成这项工作。然后该变量是main函数的本地变量。

让你的程序看起来像这样:

def main(args):
    # ... init ...
    for line in file:
        # ... process the line
        pass

if __name__ == "__main__":
    main(sys.argv[1:])