web-dev-qa-db-ja.com

Python-複数のスレッドから同じファイルに追加する

複数のスレッドから同じファイルに行を追加するアプリを書いています。

一部の行が改行なしで追加されるという問題があります。

これに対する解決策はありますか?

class PathThread(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def printfiles(self, p):
        for path, dirs, files in os.walk(p):
            for f in files:
                print(f, file=output)

    def run(self):
        while True:
            path = self.queue.get()
            self.printfiles(path)
            self.queue.task_done()


pathqueue = Queue.Queue()
paths = getThisFromSomeWhere()

output = codecs.open('file', 'a')

# spawn threads
for i in range(0, 5):
    t = PathThread(pathqueue)
    t.setDaemon(True)
    t.start()

# add paths to queue
for path in paths:
    pathqueue.put(path)

# wait for queue to get empty
pathqueue.join()
26
user1251654

解決策は、1つのスレッドのみでファイルに書き込むことです。

import Queue  # or queue in Python 3
import threading

class PrintThread(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def printfiles(self, p):
        for path, dirs, files in os.walk(p):
            for f in files:
                print(f, file=output)

    def run(self):
        while True:
            result = self.queue.get()
            self.printfiles(result)
            self.queue.task_done()

class ProcessThread(threading.Thread):
    def __init__(self, in_queue, out_queue):
        threading.Thread.__init__(self)
        self.in_queue = in_queue
        self.out_queue = out_queue

    def run(self):
        while True:
            path = self.in_queue.get()
            result = self.process(path)
            self.out_queue.put(result)
            self.in_queue.task_done()

    def process(self, path):
        # Do the processing job here

pathqueue = Queue.Queue()
resultqueue = Queue.Queue()
paths = getThisFromSomeWhere()

output = codecs.open('file', 'a')

# spawn threads to process
for i in range(0, 5):
    t = ProcessThread(pathqueue, resultqueue)
    t.setDaemon(True)
    t.start()

# spawn threads to print
t = PrintThread(resultqueue)
t.setDaemon(True)
t.start()

# add paths to queue
for path in paths:
    pathqueue.put(path)

# wait for queue to get empty
pathqueue.join()
resultqueue.join()
33
Kien Truong

同じ行または行の途中の新しい行に乱雑なテキストが表示されないという事実は、実際にファイルへの追加を同期する必要がない手がかりです。問題は、printを使用して単一のファイルハンドルに書き込むことです。 printは実際には1回の呼び出しでファイルハンドルに対して2つの操作を実行しており、それらの操作はスレッド間で競合しています。基本的にprintは次のようなことをしています:

file_handle.write('whatever_text_you_pass_it')
file_handle.write(os.linesep)

異なるスレッドが同じファイルハンドルでこれを同時に実行しているため、1つのスレッドが最初の書き込みを取得し、他のスレッドが最初の書き込みを取得し、2つのキャリッジリターンが連続して取得される場合があります。または本当にこれらの順列。

これを回避する最も簡単な方法は、printの使用を停止し、writeを直接使用することです。このようなものを試してください:

output.write(f + os.linesep)

これはまだ危険なようです。同じファイルハンドルオブジェクトを使用し、その内部バッファーをめぐって競合するすべてのスレッドでどのような保証が期待できるかわかりません。個人的にidで問題全体を回避し、すべてのスレッドに独自のファイルハンドルを取得させます。また、書き込みバッファフラッシュのデフォルトはラインバッファリングであるため、これが機能することにも注意してください。ファイルへのフラッシュを実行すると、os.linesepで終了します。ラインバッファーを使用するように強制するには、openの3番目の引数として1を送信します。次のようにテストできます。

#!/usr/bin/env python
import os
import sys
import threading

def hello(file_name, message, count):
  with open(file_name, 'a', 1) as f:
    for i in range(0, count):
      f.write(message + os.linesep)

if __name__ == '__main__':
  #start a file
  with open('some.txt', 'w') as f:
    f.write('this is the beginning' + os.linesep)
  #make 10 threads write a million lines to the same file at the same time
  threads = []
  for i in range(0, 10):
    threads.append(threading.Thread(target=hello, args=('some.txt', 'hey im thread %d' % i, 1000000)))
    threads[-1].start()
  for t in threads:
    t.join()
  #check what the heck the file had
  uniq_lines = set()
  with open('some.txt', 'r') as f:
    for l in f:
      uniq_lines.add(l)
  for u in uniq_lines:
    sys.stdout.write(u)

出力は次のようになります。

hey im thread 6
hey im thread 7
hey im thread 9
hey im thread 8
hey im thread 3
this is the beginning
hey im thread 5
hey im thread 4
hey im thread 1
hey im thread 0
hey im thread 2
4
Kevin Kreiser

そして、多分それらはあるべきではないいくつかの改行?

共有リソースには一度に複数のスレッドがアクセスしないようにしてください。そうしないと、予期しない結果が発生する可能性があります(スレッドの使用中に「アトミック操作」を使用して呼び出されます)。

少し直感的にこのページを見てください: Pythonのスレッド同期メカニズム

1
SpiXel