バッファリングを無効にする
ブロックバッファリングは、出力を書き込みバッファに貯めて、蓄積した分を一度に書き出すのでパフォーマンス面では非常に効率的でした。
ローカルのファイルに書き出す場合はこれで問題になることはまずありません。問題が発生するのは、他のプロセスと通信するときです。Perlは、プロセス間通信の機能を使ってローカルの他のプロセスと通信したり、ソケットという機能を使ってネットワーク越しの他のプロセスと通信することができます。
このときブロックバッファリングが行われていると、自分は相手に対してデータを送ったつもりでも送れていないということが起こります。自分自身の書き込みバッファに残ったままになるわけです。
これを解決するために、ブロックバッファリングをコマンドバッファリングに変更する方法があります。コマンドバッファリングを有効にするには特殊変数「$|」を1にセットします。
$| = 1;
サンプル
以下はコマンドバッファリングに切り替えるサンプルです。
use strict; use warnings; # 書き込み用のファイル( 上書きするので注意 ) my $file = "sample20080812.txt"; open(my $fh, ">", $file) or die "Cannot open $file: $!"; # デフォルトの出力ハンドルを変更 my $oldfh = select $fh; # コマンドバッファリングを有効にする。 $| = 1; # 元のファイルハンドルに選択しなおす。 select $oldfh; print $fh "遅延させないで書き込む\n"; print "この時点で書き込まれました。サイズは" . -s $file . "バイトです。\n"; sleep 3; close $fh or die "Cannot close $file";
(参考)sleep関数、open関数、close関数、die関数
コマンドバッファリング
Perlは、まったくバッファリングしない書き込みというのはサポートしていません。1文字づつ書き込むということが必要になることはないからです。変わりに、printというコマンドが終わった時点で、書き込みを行うコマンドバッファリングを使うことができます。
print関数を記述して、次の文を実行する前には、出力に書き出されるということが保障されます。
コマンドバッファリングは、対話型の通信を行うときに使うことが多いです。たとえば、相手にデータを送って、返答を待ち構えるとします。ネットワーク越しにデータを送るには、ソケット(一種のファイルハンドル)というものに書き込んで、返答を待ちます。このときもしブロックバッファリングが有効になっていると、書き込んだつもりでも実は書き込みバッファに溜め込んだままで、実際には書き込んでいないということになります。
これを回避するために、コマンドバッファリングは使われます。
コード解説
(1)コマンドバッファリングを有効にする
# デフォルトの出力ハンドルを変更 my $oldfh = select $fh; # コマンドバッファリングを有効にする。 $| = 1; # 元のファイルハンドルに選択しなおす。 select $oldfh;
特殊変数 $| に1を設定することで、コマンドバッファリングが有効になります。ここで、注意しなければいけないのは、$| に対する操作は、「デフォルトの出力先」に対する操作になるということです。
「デフォルトの出力先」というのは、print関数でファイルハンドルを省略したときの出力先のことです。デフォルトの出力先はSTDOUTです。
$fh への書き込みに対してコマンドバッファリングを有効にするには、select 関数で「デフォルトの出力先」を変更してから、$| の値を1にします。
select関数は、現在のデフォルトの出力先を返却するので、保存しておいて、$| を操作した後、元に戻しておきます。