1

私はいつでも起動または停止できるプログラムを持っています。このプログラムは、Webページからデータをダウンロードするために使用されます。最初に、ユーザは.csvファイル内に多数のウェブページを定義し、その.csvファイルを保存してプログラムを開始する。そのプログラムは.csvファイルを読み取り、それをジョブのリストに変換します。次に、ジョブは並行して機能する5つの別個のdownloader機能の間で分割されますが、ダウンロードに時間がかかることがあります。Python 3:複数のプロセスから同じファイルに書き込む方法を教えてください。

downloader(5つあります)がウェブページのダウンロードを完了した後、.csvファイルを開き、リンクを削除する必要があります。このようにして、時間の経過とともに、.csvファイルは小さくなります。問題は、2つのdownload関数が同時に.csvファイルを更新しようとし、プログラムがクラッシュすることがあることです。私はこれにどのように対処できますか?

+2

これは、問題を処理するのに特に困難な方法のようです。これらの仕事は何ですか?おそらくそれが問題を処理する最良の方法です。 csvを一度消費してジョブを作成します(たぶん、データベースや別々のファイルに保存されます)。それらを管理します。csvのことをしたいのであれば、作業者からの応答が送信され、csvファイルが更新される単一のエンティティ(おそらくジョブを扱うマスタープログラム)によってのみ管理されるべきです。 – tdelaney

+1

あなたはしません。複数の(平行な)コントロールポイントからの副作用を処理することは、災害のためのレシピです。それを動作させる*唯一の方法であれば、あなたの現在の答えのいくつかが示唆しているようにロックを実装できますが、ここではそうではないようです。マネージャ/ワーカーパターンを実装し、マネージャにIOを渡し、ジョブをワーカーに渡し、ワーカーから結果を受け取らせる必要があります。 –

答えて

3

これはあなたのproject from yesterdayあなたは既にメモリにダウンロードリストを持っているの継続であれば - ちょうど彼らのプロセスは、ダウンロードを終えると、ロードされたリストからエントリを削除し、のみ、入力ファイル上の全リストを書き留め一度あなたは 'ダウンローダ'を終了しています。常に変更を書き留める必要はありません。

「ダウンローダ」の実行中にもURLがダウンロードされたときに(外部プロセスから)知りたい場合は、そのダウンロードが成功するたびにdownloaded.datに改行が書き込まれます。

もちろん、どちらの場合でも、メインプロセス/スレッド内から書き込みを行い、mutexについて心配する必要はありません。

UPDATE - ここでは昨日と同じコードベースを使用して、追加のファイルでそれを行う方法は次のとおりです。

def init_downloader(params): # our downloader initializator 
    downloader = Downloader(**params[0]) # instantiate our downloader 
    downloader.run(params[1]) # run our downloader 
    return params # job finished, return the same params for identification 

if __name__ == "__main__": # important protection for cross-platform use 

    downloader_params = [ # Downloaders will be initialized using these params 
     {"port_number": 7751}, 
     {"port_number": 7851}, 
     {"port_number": 7951} 
    ] 
    downloader_cycle = cycle(downloader_params) # use a cycle for round-robin distribution 

    with open("downloaded_links.dat", "a+") as diff_file: # open your diff file 
     diff_file.seek(0) # rewind the diff file to the beginning to capture all lines 
     diff_links = {row.strip() for row in diff_file} # load downloaded links into a set 
     with open("input_links.dat", "r+") as input_file: # open your input file 
      available_links = [] 
      download_jobs = [] # store our downloader parameters + a link here 
      # read our file line by line and filter out downloaded links 
      for row in input_file: # loop through our file 
       link = row.strip() # remove the extra whitespace to get the link 
       if link not in diff_links: # make sure link is not already downloaded 
        available_links.append(row) 
        download_jobs.append([next(downloader_cycle), link]) 
      input_file.seek(0) # rewind our input file 
      input_file.truncate() # clear out the input file 
      input_file.writelines(available_links) # store back the available links 
      diff_file.seek(0) # rewind the diff file 
      diff_file.truncate() # blank out the diff file now that the input is updated 
     # and now let's get to business... 
     if download_jobs: 
      download_pool = Pool(processes=5) # make our pool use 5 processes 
      # run asynchronously so we can capture results as soon as they ar available 
      for response in download_pool.imap_unordered(init_downloader, download_jobs): 
       # since it returns the same parameters, the second item is a link 
       # add the link to our `diff` file so it doesn't get downloaded again 
       diff_file.write(response[1] + "\n") 
     else: 
      print("Nothing left to download...") 

私は、ファイルを使用するために、コメントに書いたように全体的なアイデアは、ありますダウンロードされたリンクをダウンロードして保存し、次回の実行時にダウンロードしたリンクをフィルタリングして入力ファイルを更新します。そうすれば、強制的に強制終了しても、中断したところから再開します(部分ダウンロードを除く)。

+0

です。私はファイルを更新する必要があります。これは、約40,000件のジョブがそれぞれ約1分かかることがあり、このプログラムの途中でコンピュータがシャットオフする(または中断される)可能性が高いからです。したがって、ファイルを更新する必要があると思います。 – user1367204

+0

@ user1367204だから、各ワーカープロセスで40kのエントリcsvを解析し、削除する予定の* 1つのエントリを見つけてファイルを上書きする必要がありますか?ホラーショーのように聞こえる。いくつかのより優れた実装がありますが、そのうちの1つは、csvをそのまま維持し、「done」リストを作成することに関するzwerの第2段落です。 –

+0

@ user1367204 - 'diff'ファイルを使用する - そのファイルでダウンロードしたリンクごとに新しい行を作成し、次回、リンクを '入力'ファイルにロードした後にダウンローダを起動すると、 'ファイルから' diff 'リンクを' input 'リンクから削除し、このフィルタリングされたリストで' input 'ファイルを保存し、' diff 'ファイルをブランクにします。私はあなたがそれをより視覚化する必要がある場合、少し後に擬似コードを書くでしょう... – zwer

0

pythonでファイルをロックする方法を調べてください。ファイルをロックすると、ファイルをロック解除して変更するまで次のプロセスが待機します。ファイルをロックすることはプラットフォームに依存しますので、あなたが使用しているOSに対応した方法を使用する必要があります。 osを理解する必要がある場合は、このようなswitch文を使用します。

import os 

def my_lock(f): 
    if os.name == "posix": 
     # Unix or OS X specific locking here 
    elif os.name == "nt": 
     # Windows specific locking here 
    else: 
     print "Unknown operating system, lock unavailable" 

それから私はthis articleを見て、あなたがロックを実装する方法を正確に把握します。

+0

なぜカスタムロックを実装するのですか?この目的のために['multiprocessing.Lock'](https://docs.python.org/3/library/multiprocessing.html?highlight=multiprocessing.lock#multiprocessing.Lock)が存在します。 –

0

マルチプロセッシングライブラリの「ロック」を使用して、ファイルの操作をシリアル化します。

ロックを各プロセスに渡す必要があります。各プロセスは、ファイルを開く前にロックを '取得'し、ファイルを閉じた後にロックを '解放'する必要があります。

https://docs.python.org/2/library/multiprocessing.html

関連する問題