Pythonのsleepは寝ていなかった
すごい、飲みすぎいて頭イタなっているときに妙なtweetがながれてきて、忘れていたことを思い出し…
それは、 Pythonのsleepを呼び出すと処理が停止してしまう という現象だったのですが…
問題の処理内容
HaskellのAsyncライブラリを弄っていたときに遭遇した現象で以下のような処理をサンプルで作成しました
- HaskellのcreateProcessで子プロセスのPythonコードをexecしPipeでStdin,Stdoutをつなげる
- Python側でテキストファイルを読み込み1行ずつStdoutへ出力
- Haskell側はStdinからPythonが書き込んだデータを読み込み出力する
という簡単なものです、以下にコードを貼っておきます。
Haskellのコード
createProcessで子プロセスを作成しPipeをつないで、readHandle関数をmapConcurrentlyで並列に動かすという単純なサンプルです
{-# LANGUAGE ScopedTypeVariables #-}
import System.IO
import System.Process
import Control.Concurrent.Async
import Control.Exception
main :: IO ()
main = do
  (_, Just hout, Just herr, hproc) <-
    createProcess
      (shell "./extern_command.py")
      { std_out = CreatePipe
      , std_err = CreatePipe
      }
  res <- mapConcurrently (readHandle 0) [hout, herr]
  putStrLn "=== res"
  mapM_ (putStrLn . show) res
stdOut :: Handle -> IO Int
stdOut = readHandle 0
stdErr :: Handle -> IO Int
stdErr = readHandle 0
readHandle :: Int -> Handle -> IO Int
readHandle c h = do
  end <- hIsEOF h
  case end of
    False -> do
      readInfo <- hGetLine h
      putStrLn readInfo
      readHandle (c+1) h
    _ -> return cPythonのコード
単純にファイルをOpenし1行づつ返すものです
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import sys
import os
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering=1)
def main():
    path = "test.txt"
    with open(path) as f:
        for s in f:
            time.sleep(2)
            sys.stdout.write(s)
        
if __name__ == '__main__':
    main()test.txtファイル
ああああいいいいうううう
ええええおおおおかかかか
ききききくくくくけけけけ実行
2秒ごとに1行づつ送り返してきます
~/Async $ runghc Async1.hs 
ああああいいいいうううう
ええええおおおおかかかか
ききききくくくくけけけけ
=== res
3
0
~//Async $ 結果として、どこも悪くなく、Python3の ディフォルト の バッファリングモードがブロックバッファリング ということで、 バッファリングするデータが多いと フラッシュまでに時間がかかり止まったように見えたということでした。
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering=1)これを入れることによって、行バッファリングになるそうです、sleepつかってるのでそこでフリーズしたのかと勘違いしました。
ちなみに以下のPHPのコードは
#!/usr/bin/php
<?php
$f = fopen("test.txt", "r");
if($f) {
    fputs(STDERR, ">>>=== STDERR 1 put!\n");
    while($line = fgets($f)) {
        sleep(2);
        fputs(STDOUT,$line);
    }
    fputs(STDERR, ">>>=== STDERR 2 put!\n");
}
?>これは行でフラッシュしてきます。
個人的には行の方がイメージ的に使いやすいのではないかと思いますが、この辺の動作はPython2から変わっているのかな、思い出して 「あぁー」 ってなったのですがすっきりしてよかったです。
Posted on 2021-05-26 07:18:26

