Pythonのsleepは寝ていなかった

すごい、飲みすぎいて頭イタなっているときに妙なtweetがながれてきて、忘れていたことを思い出し…

それは、 Pythonのsleepを呼び出すと処理が停止してしまう という現象だったのですが…

問題の処理内容

HaskellのAsyncライブラリを弄っていたときに遭遇した現象で以下のような処理をサンプルで作成しました

  1. HaskellのcreateProcessで子プロセスのPythonコードをexecしPipeでStdin,Stdoutをつなげる
  2. Python側でテキストファイルを読み込み1行ずつStdoutへ出力
  3. 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 c

Pythonのコード

単純にファイルを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

はじめまして

静岡の極東の浜辺で何かをやっている人間です、基本的に季節を問わずBBQとオフロードをバイクで走るのがが大好きです、暇があれば何かを焼いているか走っています。どこかの浜で焼いている姿をみたら、gentooの話もで飲みながらしましょう...

Posted

Amazon

tags

日本酒池 広井酒店 やがら やっぱた 刺身 丸干し 東京マラソン fpm php82 servant thread spawn Rust Oracle Linux 8 microcode firmware linux openzfs zfs gitea 麒麟 真野鶴 金鶴 日本酒 docker oracle pod podman cli virtualbox VirtualBox epub mobi calibre mask lens ワンライナー php redmine Linux Oracle Map OMap omap map BBQ カテゴリ管理 カテゴリ timestamp date oracle database string 麦焼酎 ダービー process 磨き蒸留 広井酒店、日本酒 芋焼酎 焼酎 ゆるキャン 広井酒店、日本酒池 spring framework java persistent spring session session spring hdbc-odbc persistent-odbc odbc day utctime スィート レマンの森 elm初期化 elm バイク xlr80 esqueleto database xl2tpd strongswan vpn l2tp ipsec 正月 ゲーム grub nginx systemctl portage 豚骨 圧力鍋 yesod-auth-hashdb yesod-auth yesod