0

私はperlのスレッドの初心者です。

プロジェクトのリストを含むファイルがあり (各プロジェクトは別の行にあります)、それらのプロジェクトを並行してビルドしたいと考えています。現在、各スレッド:

  1. ファイルを「読み取り」モードで開きます
  2. いくつかのプロジェクト (= いくつかのファイル行) のリストを保存します
  3. ファイルを閉じます
  4. ファイルを再度開きます-「書き込み」モードとして
  5. 選択された行なしで書き直します

各スレッドがファイルにアクセスする唯一のスレッドであることを確認するために、セマフォを使用しようとしています。

何らかの理由で、スレッドの衝突が発生し、何が間違っているのかわかりません。

(各ビルドの現在の時刻も取得する「レポート」で)異なるスレッドが「共有」ファイルから同じプロジェクトを選択することがわかります(たまにしか発生しませんが、それでも..)

私の $semaphore 宣言が「私の」変数として合法であるかどうかさえわかりません。

どんな助けでも本当に感謝します!!

ありがとう。


ここに私のコードの一部があります:

    my $semaphore = Thread::semaphore->new() ;

sub build_from_targets_list{

    #...
    open(REPORT, "+>$REPORT_tmp");  # Open for output
    #....
    @threads =();


    for ($i = 0; $i < $number_of_cores; $i++){
        my $thr = threads->new(\&compile_process, $i,*REPORT);
        push @threads, $thr;
    }

    $_->join for @threads;
    close (REPORT);
}
### Some stuff..


sub compile_process{

    *REPORT = shift(@_);
    #...

    while (1){
        $semaphore->down();
        open (DATA_FILE, $shared_file);
        flock(DATA_FILE, 2);
        while ($record = <DATA_FILE>) {
            chomp($record);
            push(@temp_target_list,$record);    
        }


        # ... choose some lines (=projects)...
        # remove the projects that should be built by this thread:
        for ($k = 0; $k < $num_of_targets_in_project; $k++){            
            shift(@temp_target_list);

        }

        close(DATA_FILE);       
        open (REWRITE,">$shared_file");

        flock(REWRITE, 2);

        seek(REWRITE, 0, 0); 
        foreach $temp_target (@temp_target_list){

            print REWRITE "$temp_target\n";

        }

        close (REWRITE);

        ## ... BUILD selected projects...

        $semaphore->up();
        }
}
4

1 に答える 1

1

最初に、ファイルの扱い方の基本的なクリーンアップを行います。単純なファイルの問題である場合、スレッドの問題をデバッグしようとしても意味がありません。

ファイル コマンド (open、close、flock、seek など) が成功することを確認する必要があります。or dieそこに s を貼り付けるか、 use autodie.

2 つ目は、ハードコードされた定数を flock に使用することです。これらはシステムに依存しており、どちらのモード 2 かを覚えておくのは困難です。 Fcntlは定数を提供します。

排他ロックを使用して読み取り用にデータ ファイルを開いています (通常、2 は排他ロックです)。それはおそらく共有ロックであるはずです。これにより問題が発生することはほとんどありませんが、スレッドが不必要にブロックされる原因となります。

最後に、グローバル スコープのグロブの代わりにレキシカル ファイルハンドルを使用します。これはチャンスを減らします

use Fcntl qw(:flock);
use autodie;

open (my $data_fh, $shared_file);
flock($data_fh, LOCK_SH);

ちなみに、seek $fh, 0, 0書き込み用にファイルを開いた後は不要です。Fcntl を使用して定数を取得します。

追加のバグは、あなたが渡している$i, *REPORTが、最初の引数であるcompile_processと考えていることです。*REPORTまた、グローバル ファイルハンドルの使用は、それを渡すのが冗長であることを意味します。レキシカル ファイルハンドルを使用してください。

これで邪魔になりません。基本的なアルゴリズムに欠陥があるようです。 compile_process各スレッドがデータ ファイル全体をスレッド ローカル配列@temp_target_listに読み取り、そのローカル配列から一部をシフトして残りを書き出すようにします。@temp_target_listスレッドごとなので、調整はありません。共有$num_of_targets_in_projectされていて、何らかのオフスクリーン調整を行っている場合を除きますが、それは示されていません.

ファイルベースのロックは、常に地獄のようなものです。スレッドには、調整のためのはるかに優れたメカニズムがあります。これを行うには、もっと簡単な方法があります。

ファイルが大きすぎないと仮定して、各行を共有配列に読み込みます。次に、各スレッドがその配列から作業するアイテムを取得するようにします。配列は共有されるため、各要素が削除されると、配列はすべてのスレッドに対して更新されます。何かのようなもの...

use strict;
use warnings;
use autodie;

use threads;
use threads::shared;

my $Max_Threads = 5;
my @Todo : shared;

open my $fh, "<", $work_file;
@Todo = <$fh>;
close $fh;

my @threads;
for (1..$Max_Threads) {
    push @threads, threads->new(\&compile_process);
}

$_->join for @threads;

sub compile_process {
    while( my $work = shift @Todo ) {
        ...do whatever with $work...
    }
}

ファイルが大きすぎてメモリに保持できない場合は、Thread::Queueを使用して作業項目のキューを作成し、動的に追加できます。

于 2012-10-22T19:09:52.200 に答える