sleepコマンドなしでbashで忙しい待機を避ける

sleepコマンドなしでbashで忙しい待機を避ける

次のようにして、bashで条件がtrueになるまで待つことができることを知っています。

while true; do
  test_condition && break
  sleep 1
done

ただし、繰り返し(睡眠)ごとに1つのサブプロセスが作成されます。次のようにすると、この問題を回避できます。

while true; do
  test_condition && break
done

しかし、CPUを多用します(Busy Waiting)。サブプロセスや忙しい待機を避けるために、次の解決策を思いつきましたが、それは見た目に悪いです。

my_tmp_dir=$(mktemp -d --tmpdir=/tmp)    # Create a unique tmp dir for the fifo.
mkfifo $my_tmp_dir/fifo                  # Create an empty fifo for sleep by read.
exec 3<> $my_tmp_dir/fifo                # Open the fifo for reading and writing.

while true; do
  test_condition && break
  read -t 1 -u 3 var                     # Same as sleep 1, but without sub-process.
done

exec 3<&-                                # Closing the fifo.
rm $my_tmp_dir/fifo; rmdir $my_tmp_dir   # Cleanup, could be done in a trap.

注:通常、read -t 1 varfifoはstdinを消費するため、fifoなしでは使用できず、stdinが端末またはパイプでない限り機能しません。

よりエレガントな方法でサブプロセスと忙しい待機を避けることはできますか?

答え1

bash最新バージョン(最小v2)では、組み込み機能を実行時にロードできます(enable -f filename commandname. これらのロード可能な組み込み機能の多くはbashソースと共に配布され、sleepその中にあります。もちろん、可用性はオペレーティングシステムごと(マシンごとに)異なるたとえば、openSUSEでは、これらの組み込み機能はパッケージを介して配布されます。bash-loadables

答え2

内部ループに多くのサブプロセスを作成するのは悪いことです。sleep毎秒1つのプロセスを作成します。大丈夫です。

while ! test_condition; do
  sleep 1
done

外部プロセスを本当に避けたい場合は、FIFOを開く必要はありません。

my_tmpdir=$(mktemp -d)
trap 'rm -rf "$my_tmpdir"' 0
mkfifo "$my_tmpdir/f"

while ! test_condition; do
  read -t 1 <>"$my_tmpdir/f"
done

答え3

私は最近これをする必要がありました。私は外部プログラムを呼び出さずにbashを永遠に眠らせる次の関数を思いつきました。

snore()
{
    local IFS
    [[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null ||
    {
        # workaround for MacOS and similar systems
        local fifo
        fifo=$(mktemp -u)
        mkfifo -m 700 "$fifo"
        exec {_snore_fd}<>"$fifo"
        rm "$fifo"
    }
    read ${1:+-t "$1"} -u $_snore_fd || :
}

注:以前は毎回ファイルディスクリプタを開いて閉じるバージョンを公開しましたが、一部のシステムでは、毎秒数百回この操作を実行すると、最終的に動作が停止することがわかりました。したがって、新しいソリューションは関数呼び出し間でファイル記述子を保持します。 Bashはとにかく終了時にそれをクリーンアップします。

これは/ bin / sleepのように呼び出すことができ、要求された時間の間スリープモードになります。引数なしで呼び出されると永久に中断されます。

snore 0.1  # sleeps for 0.1 seconds
snore 10   # sleeps for 10 seconds
snore      # sleeps forever

私のブログにはあまりにも多くの詳細を含む記事があります。

答え4

またははksh93組み込みシェルなので、他のオプションは代わりにこれらのシェルを使用することmkshです。sleepbash

zsh与えられた100分の1秒間休止するzselect組み込み関数もあります。zmodload zsh/zselectzselect -t <n>

関連情報