странное поведение в питоне - PullRequest
0 голосов
/ 05 апреля 2010

Теги могут быть неточными, так как я не уверен, где проблема.

У меня есть модуль, в котором я пытаюсь прочитать некоторые данные из сокета и записать результаты в файл (приложение). Это выглядит примерно так (только соответствующие части)

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 ]

1 голос
/ 05 апреля 2010

Когда вы имели (упрощение к важной части)

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

foo, не имея локальной переменной line, использовали переменную global с таким именем... который оказывается одним набором (в главном потоке) на for line in file:.

В частности, я ожидаю, что общее количество записанных строк будет в порядке: в каждой строке по одному потоку (странная архитектура(BTW) и каждый поток записывает одну строку ... единственная проблема заключается в том, какая строка записывает каждый поток.

По вашему мнению, первый поток пишет первую строку, второй потокпишет вторую строку и т.д .;но на самом деле каждый поток напишет строку, связанную с глобальным именем line в критический момент, когда поток вызывает writeResults.Таким образом, некоторые строки могут в конечном итоге записываться несколько раз, другие - не записываться.

Например, предположим, что основной поток работает быстрее на столько, чтобы запустить все подпотоки до того, как какая-либо из них действительно приступит к записи.В этом случае значение last , принятое глобальным именем line (т.е. последней строкой в ​​файле), будет записано всеми потоками.

Обратите внимание, чтодаже в «исправленной» версии нет гарантии относительно порядка , в котором записываются различные строки, что является частью того, что делает эту архитектуру странной - обычно, поскольку строки располагаются в определенном порядке, выхотел бы сохранить этот порядок на выходе.Я полагаю, ваш случай приложения достаточно специфичен, чтобы не нуждаться в этом ограничении, но я все еще озадачен тем, что вам нужно столько потоков, когда вы читаете из одного файла и записываете в один файл! -)

1 голос
/ 05 апреля 2010

Чтобы избежать глобальной переменной с именем line, вы должны написать функцию с именем main, которая выполняет эту работу. Тогда переменная является локальной для функции main.

Сделайте так, чтобы ваша программа выглядела так:

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

if __name__ == "__main__":
    main(sys.argv[1:])
1 голос
/ 05 апреля 2010

Вы действительно должны использовать 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 глобальным, и каждый поток написал его один раз.

...