Perlトリビアルメモ 1(ファイルロック、正規表現、foreach)

今日、これ(中村屋)の紹介を受けたんですが、こういうのはもっと早く報告していただかないと困りますね。本家じゃないようですが、本家消えてるようで。早めに世界遺産に登録したほうがいいんじゃないでしょうか?

今回はPerlのメモ。最近Perl触ってないので、思い出すため。ノスタルジー。たいしたことは書き(け)ません。

1.ファイルのロック
ファイルのロックは、今時flock()。昔はWindowsサーバーとかで使えなかったりしてsymlinkやmkdirで代用する手法がメジャー(?)だった。今はWindowsでもflock()使えるし、大体においてflock()でOK、ってどっかで読んだ。
問題はロックするタイミング。
失敗例:


open(FILE, " > file.txt");
flock(FILE, 2);

ロックする前にファイルサイズが0になるので×。追加書き込みモードで開く。


open(FILE, "+< file.txt");
flock(FILE, 2);

もちろん、新規にファイルを作成する場合は最初のでOK。
もっと問題なのはデッドロック。CGIなどで複数のファイルを読み書きする必要がある場合で、複数の同時アクセスがあった場合、一個一個のファイル操作で無頓着にロックを行っているとデッドロックになる。
例えば、ユーザの1アクセスでファイルAとファイルBを読み書きする場合。次のような順番で処理が行われると危険。

①ユーザFooがファイルAをロックして読み書き開始。
②ユーザBarがファイルBをロックして読み書き開始。
③ユーザFooはファイルAをロックしたままファイルBを読み書き開始。
④ユーザBarはファイルBをロックしたままファイルAを読み書き開始。

この場合、③でファイルBはBarによってロックされているので、ロックが解除されるまでFooは待とうとします。同様に④でファイルAはFooによってロックされているので、ロックが解除されるまでBarは待とうとします。ここでどちらも処理が止まり、どのファイルもロックが解除されることはありません。デッドロック。
①の後、FooがとっととファイルAを開放すりゃいいじゃないかという案もありますが、ファイルAを更新するためにはファイルBの内容が必要ということもあります。いったんファイルAを開放し、ファイルBを読み込んだ後改めてファイルAを更新するという手順を踏むと、ファイルAを開放している間に他のプロセスによってファイルAが更新されてしまい、データの連続性が失われる、とかそういう話です。
こういう場合はどうしたらいいかというと、簡単には2通りだったと思います。

○どんなアクセスでもファイルAを読み込んでからファイルBを読み込む。
○処理全体でロックしちゃう。

最初の方法だと、②でユーザBarはファイルBを操作する前にファイルAを操作しなくてはなりません。しかし、ファイルAはロックされているため、ユーザFooの処理が終わるまではBarは手出しできないという寸法です。
しかし、実際にはファイルAやBがユーザ情報ファイルだったりする場合、Barが最初に読み込むべきはファイルBでしょう。

そういうわけで、後者の方法が手っ取り早いです。現在のネット航海時代のユーザ情報ファイルアクセスもこの方式(のはず)です。
この方法ではファイルロック用に別途ファイルを用意しておいて、プロセス開始後、目的のファイルを操作する前にこのファイルロック用ファイルをロックします。その後、どのファイルでも何個でも適当にアクセスします。プロセス終了時にファイルロック用のファイルのロックを解除します。こうしておけば、処理中はファイルロック用ファイルがずっとロックされた状態になり、他のプロセスは手出しできないようになります。ネット航海時代ではシステム上、複数のユーザ情報ファイルにアクセスする必要があるためこのようにしています。(パフォーマンスは下がると思いますが。というか「DBを使え」という話も・・・。一度DB化したんですが、めんどくさくてリリースしませんでした。)

2.正規表現
同じ正規表現を繰り返し利用する場合は、正規表現をコンパイルしたほうが早いらしいです。正規表現のコンパイルにはqrを使います。


$pattern = qr/abc(\\d+)efg/;

foreach (@text) {
    if ($_ =~ /$pattern/) {
        print $1;    # 後方参照もOK
    }
}

3.foreach()
PHPやってるから思う疑問かもしれません。foreachでの変数の変更は有効か。


@text = ("aaa", "bbb");

foreach (@text) {
	$_ = "ccc";
}

foreach (@text) {
	print "$_\n";
}

結果:


ccc
ccc

ちなみにPHPでやると


$text = array("aaa", "bbb");

foreach ($text as $one) {
	$one = "ccc";
}
print_r($text);

結果:


aaa
bbb

PHP5だと以下のように書けば、


$text = array("aaa", "bbb");

foreach ($text as &$one) {   // &が付いた
	$one = "ccc";
}
print_r($text);

結果:


ccc
ccc

以上

コスミー について

昔(?)はゲーム作ってました。 今もなんか作ろうとしています。
カテゴリー: Perl, PHP, Tips パーマリンク

Perlトリビアルメモ 1(ファイルロック、正規表現、foreach) への3件のフィードバック

  1. o-taki のコメント:

    一応 ruby の場合

    text = [“aaa”, “bbb”]
    text.each{|t| t = “ccc”}
    p(text) # => [“aaa”, “bbb”]

    中身を変えたい場合は map! を使う
    text = [“aaa”, “bbb”]
    text.map!{|t| t = “ccc”}
    p(text) # => [“ccc”, “ccc”]

  2. コスミー のコメント:

    > o-taki
    ナイス補足です。
    ちなみに、Javaだと。

    
    public class ForEachTest {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            ArrayList textList = new ArrayList();
           
            textList.add("aaa");
            textList.add("bbb");
           
            for (String one : textList) {
                one = "ccc";
            }
           
            System.out.println("---- ArrayList ----");
            for (String one : textList) {
                System.out.println(one);
            }
           
           
            ArrayList objs = new ArrayList();
           
            objs.add(new ObjectString("aaa"));
            objs.add(new ObjectString("bbb"));
           
            for (ObjectString one : objs) {
                one.data = "ccc";
            }
           
            System.out.println("---- ArrayList ----");
            for (ObjectString one : objs) {
                System.out.println(one.data);
            }
           
           
            String[] text = {"aaa","bbb"};
           
            for (String one : text) {
                one = "ccc";
            }
           
            System.out.println("---- String[] ----");
            for (String one : text) {
                System.out.println(one);
            }
    
        }
       
        static class ObjectString {
            public String data;
           
            public ObjectString(String a) {
                this.data = a;
            }
        }
    
    } 
    

    結果は

    
    ---- ArrayList ----
    aaa
    bbb
    ---- ArrayList----
    ccc
    ccc
    ---- String[] ----
    aaa
    bbb
    
  3. 信用第一 のコメント:

    特別価格

コメントを残す

メールアドレスが公開されることはありません。