job の中で Start-Process すると死ぬ

start-job で script block を走らせて、その中で start-process して bat で stdout に何かあると死が待ち受けています。 また bat の中で python を呼び出していると追死(ついし)が待ち構えています。 2回死ね!

@rem test1.bat
echo WANWAN
python -c "print('nyoro-nyoro')"
# test1.ps1

$script = {
    echo "start"
    Start-Process -Wait -NoNewWindow -FilePath "${PWD}\test1.bat"
    echo "おわったにゃん♪"
}

Start-Job -ScriptBlock $script
# 実行
PS> .\test1.ps1
PS> Receive-job ${ID}
start

おわってないにゃん…

いろいろ検証したところ、やはり Start-Process で死んでる感じでした

解決方法

-RedirectStandardOutput のオプションを追加して stdout をファイルに落とします。

  • job は "マイドキュメント" が起点になります。ScriptBLock 内で cd してないので stdout.txt はマイドキュメントに落ちます。このサンプルは良くない
  • 場合によっては -RedirectStandardError "stderr.txt" もお忘れなく!
  • -RedirectStandardInput "emptry.txt" もあった方が死を回避出来るかも
# test1.ps1

$script = {
    echo "start"
    Start-Process -Wait -NoNewWindow -FilePath "${SCRIPT_PATH}\test1.bat" -RedirectStandardOutput "stdout.txt"
    echo "おわったにゃん♪"
}

Start-Job -ScriptBlock $script
# 実行
PS> .\test1.ps1
PS> Receive-job ${ID}
start
おわったにゃん♪

# ※ bat の結果は stdout.txt に落ちる

おわったにゃん!!

おわったにゃん!!!!

やったねタエちゃん!

たぶんだぶん

job だと stdout が無いが故に なんか調子悪いんだろうなぁとは予測するものの、ScriptBlock 直下の echo の結果は Receive-Job で得られてるんですよねぇ… さっぱりわからねぇよ Microsoft さんよォ!!

あと、ファイルに落とさず OnMemory で stdout を受け取りたい感じがとてもしますけれど、PowerShell のコマンドレットの領域だけでは出来ないんじゃないかなぁ…。 -RedirectStandardOutput に pipe handle 指定出来れば良いんですけどそんな機構では無さそうな気がするというか PowerShell レイヤーで pipe handle って何よみたいな

なお、以下のようなことは出来ません。 Start-Process は stdout に何も出力しないからです! Start-Process は子プロセスの stdout は吐きません!

# だめぽ
Start-Process -Wait -NoNewWindow -FilePath "${PWD}\test1.bat" > stdout.txt

# stdout は受け取れない(-PassThru 付与で C# の Process object が取れる
$stdout = Start-Process -Wait -NoNewWindow -FilePath "${PWD}\test1.bat"

たぶんだぶん2 - python で死んだ

bat の中で python の script を実行しています。 見ての通り stdout に出すだけなのですが… stdin が無いと Start-Prcoess が帰ってきません。 別に stdin は見てないんですが stdin の入力待ちをしているような感じで世界が止まります。 おのれほむほむ!!

というわけで -RedirectStandardInput "empty.txt" で空のファイルを stdin に与えると動いてくれました。 もし python の script で stdin から何も取ってなければ中身有っても良いと思うので、C:\Windows\System32\drivers\etc\hosts とかテキトーに与えれば良い気がしますしらんけど

空のファイルで良ければ -RedirectStandardInput "nul" とか指定したい所ですが、C# だと MS-DOS 予約デバイス名は受け入れてくれないので open 出来ず Start-Process 自体が失敗するので死にます

bat の中で python とか他の CUI プログラムを読んでおらず、bat だけで完結するようであれば -RedirectStandardInput は不要かも? python が悪いだけかこれは? 謎

たぶんだぶん3

-RedirectStandardOutput が付与されて「無くて」止まる状況において、Win10 だと State=Completed で何事も無かったかのように終わったのですが…

PS> get-job

Id  Name  PSJobTypeName   State      HasMoreData     Location
--  ----  -------------   -----      -----------     --------
55  test  BackgroundJob   Completed  True            localhost

PS> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      5.1.19041.4412
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.4412
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Win11 だと State=Failed になり Receive-job したらエラーが吐かれました。(コードは全然違うんですけど)

PS> get-job
Id   Name  PSJobTypeName   State   HasMoreData     Location
--   ----  -------------   -----   -----------     --------
112  test  BackgroundJob   Failed  True            localhost

PS> receive-job 112
[localhost] バックグラウンド プロセスからのデータを処理中にエラーが発生
しました。報告されたエラー: ノード タイプが "Text" の要素は処理できませ
ん。Element および EndElement のノード タイプのみがサポートされます。。
    + CategoryInfo          : OpenError: (localhost:String) [], PSRemo
   tingTransportException
    + FullyQualifiedErrorId : 2102,PSSessionStateBroken

PS> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      5.1.22621.2428
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.22621.2428
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

-ScriptBlockXAML でうんうんみたいな記事を何処かで見かけたので、なんか XML が関係していそうな感じはしますが正直さっぱりわからねぇ!

PowerShell わからん

動画ファイルを2分固定の早回し再生動画として再エンコードする…みたいな話

「2倍再生」とか「0.5倍速再生」の例は web で見つかるんですけど 「長さ不定の動画を強制的に2分で収まる動画として再エンコードする」 みたいな例は見つからない感じがする(?)

…ので、それを実現するには以下で出来るハズ

ffmpeg -i input.mp4 -vf "setpts=${NEW_DURATION}*PTS/${ORG_DURATION}" -o new_movie.mp4
# Audio の話が抜けてる点に留意

NEW_DURATION / ORG_DURATION の単位は何でも良いです。"秒" であれば両方とも "秒" で指定すれば良いですし、”ミリ秒” でも "分" でも "frame" でも良いです。 小数点も使えますゾ!

少し悩ましいのは ORG_DURATION を元の動画ファイルからどう取得するかだと思います。 例えば NEW_DURATION を「2」とした場合、元動画の長さを分単位でどう得るか? 120秒 とした場合、秒単位で元動画の長さをどう得るか? 480frame としたらならどう得るか? …まぁ ffmpeg -i して出力される元動画情報から grep なり awk なり pythonPowerShell で script 書くなりで頑張るしか無いような気はします……?

俺の docker bridge がこんなに通信できないわけがない!

docker で新規で作った bridge が「外に出れない」「同じ bridge に繋がってる container同士で通信できない」とかいう現象に遭遇しまして… いやもう大迷走。 ワイは Windowsアプリ屋 な上に、Linux はおろか network / routing も専門分野じゃないんですけおーーー!!

というわけで、彷徨った記録です。備忘録です。もしかしたら bad know how かも… 記事書いててすごいそんな気がしてきた…

環境

  • ubuntu server 22.04 および 20.04 両方で問題が発生。
  • Hyper-V 上で運用
  • docker.io 20.10.21 // …なんですが、試行錯誤中に docker-ce とか入れて彷徨ってました。 環境としてはとても怪しい(めんご!

問題の現象

  • 新規の bridge (下側) のネットワークが極めて怪しい
    • container 同士で ping が通らない
    • 外にも ping が通らない
    • host から container の中には ping が通る
  • docker0 のネットワークの中は問題無く通信できる

現象としては こちら と同じです。 mac address は相手の container のが取れてました

俺的問題の解決方法

マシンが起動したら (docker.service ではなく) docker.socketdaemon を再起動します。

sudo systemctl restart docker #一応…
sudo systemctl restart docker.socket

何故こんなことをするのか?

こうするとどうにも docker network 一覧がごっそり変わる? 再起動前に restore される? っぽいんですよ。 network id が一新される…というより、仮想ブリッジ周りを含めて全体的に再起動前のものになるっぽい? 本当か?? 謎

$ sudo docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
0309364816a9   bridge               bridge    local
1c08f30a1771   host                 host      local
36dbe0d6fbdd   none                 null      local
63e20c2e1376   photoprism_default   bridge    local
$ sudo systemctl restart docker.socket # 再起動
$ sudo docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
2aebe8cf626f   bridge               bridge    local
2ad5f7ddefde   host                 host      local
fa9119c7fd97   none                 null      local
5deda7e225a8   photoprism_default   bridge    local

更に言うと、おそらく docker.socket を再起動する 【前】 は、docker が iptables へ自動で追加してくれるハズの rule が入ってくれないっぽく色々怪しい。 自前で適宜 rule 追加するのも厳しいし…

リンク先記事でも問題は解決出来た

上記のリンク記事にあるとおり net.bridge.bridge-nf-call-iptables0 にした所、通信できるようになりました

sudo sysctl -w net.bridge.bridge-nf-call-iptables=0

…が、この対応は良いのかなぁ…という疑問。 iptables を経由しなくなる(?)ってのは、後々の運用で別の苦労が発生するような事は無いんですかねぇ…?的な。 ワイは Windowsアプリ屋 なのでわかんなーい☆ ちぇるーん☆*1

格闘した記録

通信できねぇなぁと格闘してた格闘記録。 iptables で packet を trace。 mangle:forward で packet が lost する謎現象。 そこまで来たら nat:forward まで行ってくれてもえぇんやないの…? …という所ですごーーく怪しい感じがモリモリ出てました。

May 16 03:20:43 ubuntu2000 kernel: [284836.212921] TRACE: mangle:FORWARD:policy:1 IN=br-61ca521b8ed5 OUT=br-61ca521b8ed5 PHYSIN=veth8863771 PHYSOUT=veth2a972e0 MAC=02:42:ac:14:00:03:02:42:ac:14:00:02:08:00 SRC=172.20.0.2 DST=172.20.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=50616 DF PROTO=ICMP TYPE=8 CODE=0 ID=37 SEQ=905
May 16 03:20:44 ubuntu2000 kernel: [284837.213030] TRACE: raw:PREROUTING:policy:2 IN=br-61ca521b8ed5 OUT= PHYSIN=veth8863771 MAC=02:42:ac:14:00:03:02:42:ac:14:00:02:08:00 SRC=172.20.0.2 DST=172.20.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=50732 DF PROTO=ICMP TYPE=8 CODE=0 ID=37 SEQ=906
May 16 03:20:44 ubuntu2000 kernel: [284837.213038] TRACE: mangle:PREROUTING:policy:1 IN=br-61ca521b8ed5 OUT= PHYSIN=veth8863771 MAC=02:42:ac:14:00:03:02:42:ac:14:00:02:08:00 SRC=172.20.0.2 DST=172.20.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=50732 DF PROTO=ICMP TYPE=8 CODE=0 ID=37 SEQ=906
May 16 03:20:44 ubuntu2000 kernel: [284837.213042] TRACE: nat:PREROUTING:rule:1 IN=br-61ca521b8ed5 OUT= PHYSIN=veth8863771 MAC=02:42:ac:14:00:03:02:42:ac:14:00:02:08:00 SRC=172.20.0.2 DST=172.20.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=50732 DF PROTO=ICMP TYPE=8 CODE=0 ID=37 SEQ=906
May 16 03:20:44 ubuntu2000 kernel: [284837.213050] TRACE: nat:PREROUTING:policy:3 IN=br-61ca521b8ed5 OUT= PHYSIN=veth8863771 MAC=02:42:ac:14:00:03:02:42:ac:14:00:02:08:00 SRC=172.20.0.2 DST=172.20.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=50732 DF PROTO=ICMP TYPE=8 CODE=0 ID=37 SEQ=906
May 16 03:20:44 ubuntu2000 kernel: [284837.213057] TRACE: mangle:FORWARD:policy:1 IN=br-61ca521b8ed5 OUT=br-61ca521b8ed5 PHYSIN=veth8863771 PHYSOUT=veth2a972e0 MAC=02:42:ac:14:00:03:02:42:ac:14:00:02:08:00 SRC=172.20.0.2 DST=172.20.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=50732 DF PROTO=ICMP TYPE=8 CODE=0 ID=37 SEQ=906
May 16 03:20:45 ubuntu2000 kernel: [284838.213428] TRACE: raw:PREROUTING:policy:2 IN=br-61ca521b8ed5 OUT= PHYSIN=veth8863771 MAC=02:42:ac:14:00:03:02:42:ac:14:00:02:08:00 SRC=172.20.0.2 DST=172.20.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=50791 DF PROTO=ICMP TYPE=8 CODE=0 ID=37 SEQ=907

rp_filter = 0 すれば?見たいな話もチラ見したんですが、ワイの問題には全然関係ありませんでした :-]

雑感

インフラわからん

*1:マホマホ & ミミちゃん派です

PowerShell入門者ワイ、見事にアツい洗礼を受けて死亡

【初心者なりの結論】 stderr の扱いにクセがあるんですねぇこの子…

以下「PowerShell で stderr で受け取ったものを 2>&1 して stdin に回すと……火傷するぜ…!」という話がダラダラ書いてあります(ネタバレ


…というわけで、PowerShell を弄くってみたんですよ。 cmd.exe じゃ正直ちょっと力不足…… 高度な事を Windows 標準で行うには PowerShell しか無いじゃないと。 業務で「とてもセキュアです!」と謳う何も入れさせてくれない クソな 環境でとても役立つだろうと見込んでます

あと私の中では .NET用 のインタプリタという認識なので(ぉ)凄く頑張ればおそらく何でも出来そうなヨカン。 たぶんしゅごい

【お題】ffmpeg の出力をニャンニャンしたい

いきなり PowerShell で cmd.exe 向けのコマンド叩くんかいという話はさておき(ぉ

ffmpeg に動画を食わせて、そこから Audio の種別を取得したかったんです

cmd> ffmpeg -i test.webm
ffmpeg version N-83507-g8fa18e0 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 5.4.0 (GCC)
(~~ 略 ~~)
Input #0, matroska,webm, from '.\test,webm':
  Metadata:
    encoder         : Lavf57.66.102
  Duration: 00:03:57.56, start: -0.007000, bitrate: 1132 kb/s
    Stream #0:0(eng): Video: vp9 (Profile 0), yuv420p(tv, bt709/unknown/unknown), 1280x720, SAR 1:1 DAR 16:9, 29.97 fps, 29.97 tbr, 1k tbn, 1k tbc (default)
    Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, fltp (default)
At least one output file must be specified

から "opus" という文字列を取得したいなぁと。

cmd でやるとこんな感じでしょうか。 ffmpeg -i は stderr に出るので stdout に bind して pipe しました:

cmd> ffmpeg -i test.webm 2>&1 | grep "Audio: " | perl -pe "s/.+Audio: (\w+), .+/$1/g"
opus

うむ。

この結果を環境変数に入れて更に ニャンヤン しよう…となると、まぁちょっと面倒。 変数に代入したいだけなのに for で キャッキャクフフ しないと代入できないのはいささか面倒。というか本来の目的じゃないやんけという

bash なら backquote で括ってあげれば変数に入る。超楽

var=`ffmpeg -i test.webm 2>&1 | grep "Audio: " | perl -pe "s/.+Audio: (\w+), .+/$1/g`
# ※試してないです(酷

そして PowerShell も '=' で変数に代入できる。超楽!

# ps1 ファイルに書き込んで実行するとする
$result=ffmpeg -i test.webm 2>&1 | grep "Audio: " | perl -pe "s/.+Audio: (\w+), .+/`$1/g"
echo $result

echo "`n--- debug ---"
echo ('length=' + $result.Length)
echo $result[0]
echo $result[1]

結果:

+ $result=ffmpeg -i $in 2>&1 | grep "Audio: "  | perl -pe "s/.+Audio: ( ...
opus

--- debug ---
length=2
+ $result=ffmpeg -i $in 2>&1 | grep "Audio: "  | perl -pe "s/.+Audio: ( ...
opus

…え、なにこれ? なんか… 2行…なんです???

ps1 スクリプトから実行すると、叩いたコマンド自体を echo するんか…? とか思うも、結論としては違いました

PowerShell は stderr を NativeCommandError とやらに包むそうな

NativeCommandError とは?

正直よく分らないんですが!(ぉ

stderr なので Error的なオブジェクトに wrap して stream を管理する様子。 結果、元のプログラムの出力にエラーがあったコマンドラインを追加で格納して pipe に流すようです。 なにそれ

よって、単純に ffmpeg の出力を得て吐くだけにすると…

$result=ffmpeg -i $in 2>&1
echo $result

出力:

ffmpeg : ffmpeg version N-83507-g8fa18e0 Copyright (c) 2000-2017 the FFmpeg developers
発生場所 c:\path\to\flonyard\extmp3.ps1:13 文字:9
+ $result=ffmpeg -i $in 2>&1
+         ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (ffmpeg version ...mpeg developers:String)
   [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

  built with gcc 5.4.0 (GCC)
(~~ 略 ~~)
Input #0, matroska,webm, from '.\test.webm':
  Metadata:
    encoder         : Lavf57.66.102
  Duration: 00:03:57.56, start: -0.007000, bitrate: 1132 kb/s
    Stream #0:0(eng): Video: vp9 (Profile 0), yuv420p(tv, bt709/unknown/unknown), 1280x7
20, SAR 1:1 DAR 16:9, 29.97 fps, 29.97 tbr, 1k tbn, 1k tbc (default)
    Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, fltp (default)
At least one output file must be specified

頭の方になんか変なエラー情報付いてる… ('A`)

…というわけで、PowerShell さんは stderr を勝手に加工するようです。

結果、今回はそのエラー情報の部分に grep がマッチしてしまい予期せぬ結果が得られた形となりました。 嘘だと言ってよバーニィ

PowerShell の文化の中では PowerShell で統一しましょう

そもそも grep とか perl で抽出とか、cmd.exe 用のヤツ叩いてるからおかしいんじゃないの? と、思わなくも無く(ぉ

本来 cmd.exe から叩くようなモノに対して PowerShell から stdin に流し込もうとした場合、pipe に流れる object を toString() したようなものを流す…んだと思ってます(未調査

んじゃぁ、grep とか extract の部分も面倒くさがらずに PowerShell にしたら、pipe に流れる object を PowerShell界の流儀に従って良い感じに扱ってくれるんじゃなかろうかと思い、頑張って PowerShell に置き換えてみました:

$result = ffmpeg -i $in 2>&1 | Select-String "Audio: "
$result = $result -replace '.+Audio: (\w+),.+','$1'
echo $result

# --- 結果 ---
opus

おっ、ステキステキー

select-string による grep で error が発生した command line が unmatch だった… という可能性もおそらく… 無さそう… たぶん…… たぶん……

結論

というわけで PowerShell での stderr を cmd.exe のノリで触ると火傷するというお話でした。

今のところ致命的にハマったのはココだけですかねぇ…

イマイチまだ文化に慣れてませんけど cmd.exe よりかは楽出来そうな気はしています。 もうちょっと文化を知って慣れたい所……

link

Windows10 で Visual Studio 6.0 の MSDN ライブラリを開く

hh.exemsdnvs98.col を食わせましょう

ex)
c:\windows\hh.exe  "C:\Program Files (x86)\Microsoft Visual Studio\MSDN98\98VS\1041\msdnvs98.col"
  • なんでスタートメニューに登録されないのォ!
  • VC++ 6.0 起動 → ヘルプ からでも開けますが、VC++6.0 を閉じると ヘルプも一緒に終了してしまう。 いっぱいいっぱい悲しい
  • ちなみにショートカット名は "MSDN ライブラリ Visual Studio 6.0" です

ブログ移転予定です(ぜんぶ はてなの せいだ)

はてなダイアリーが廃止されるため、はてなブログに移転予定ですー

こちらの技術系ブログをご覧頂いている方はすでに情報入ってるような気もしますけども(ぉ



…とはいっても、はてなブログって1アカウントで複数ブログ持てるんでしたっけ…? っていう不安

Task『一体いつから… IsCompleted == true でタスクが完了したと錯覚していた?』 わい「なん…だと……」で死亡

結論:単に自分のバグだっただけなんですけども(ってか Task をニワカで使ってるのが悪い)




以下のようなコードで以下のような出力が得られました:

// C#
static void Main(string[] args)
{
    Task task = Task.Run(() => daruiTask());
    Task.Delay(5).Wait();
    Console.WriteLine($"{nameof(task.IsCompleted)} ; {task.IsCompleted}");
    task.Wait();
    Console.WriteLine($"{nameof(task.IsCompleted)} ; {task.IsCompleted}");

    Console.WriteLine($"かえる");
    Task.Delay(1000).Wait();
}

static async void daruiTask()
{
    await Task.Delay(100);
    Console.WriteLine($"おしごと完了ヾ( ゚∀゚)ノ゙");
}

/**
stdout
======
IsCompleted ; True
IsCompleted ; True
かえる
おしごと完了ヾ( ゚∀゚)ノ゙
**/

「かえる」の後に「おしごと完了ヾ( ゚∀゚)ノ゙」しやがったー!!(ガビーン …ってか、IsCompleted が 常に true とは一体ウゴゴゴゴ…

って感じなんですが、結論としては daruiTask() の戻り値が async void なのが悪いです。はい。 散々使うなよ!って言われ続けてるもの使っちゃったのが悪いです。はい。 async Task なら問題なく待ってくれます。はい。 本番コードで実装している際「戻り値不要だわ。void と…」って関数作った後に「async にしたほうがよくね!?」とか夜なべ中に軽くパニクりながら書いてたのが原因でした。はい。 すいませんでしたァ!!


↓問題ないコード

// C#
static void Main(string[] args)
{
    Task task = Task.Run(() => daruiTask());
    Task.Delay(5).Wait();
    Console.WriteLine($"{nameof(task.IsCompleted)} ; {task.IsCompleted}");
    task.Wait();
    Console.WriteLine($"{nameof(task.IsCompleted)} ; {task.IsCompleted}");

    Console.WriteLine($"かえる");
    Task.Delay(1000).Wait();
}

static async Task daruiTask()
{
    await Task.Delay(100);
    Console.WriteLine($"おしごと完了ヾ( ゚∀゚)ノ゙");
}

/**
stdout
======
IsCompleted ; False
おしごと完了ヾ( ゚∀゚)ノ゙
IsCompleted ; True
かえる
**/

async void を食わせると、無条件で IsCompleted が true 返すんですねぇ… そりゃそうか


Task でコールバックする関数を async にする意味とは?

実のところ、これがあんまり見えてないです。(上記の話題と関係あるようであんまり関係ない話なんですけど)

GUIスレッドから async な関数呼ぶのは理解できるんですよ。 「他のスレッドに処理を委譲して、俺は一旦帰ってメッセージポンプ回してくるわぁー。 よろしくなー!」って感じで。

それじゃぁ、Task でコールバックして既に他のスレッドに委譲している時に、非同期関数で更に別のスレッドに委譲しても良い状況にするとはなんぞやと。 別に GUI が固まるわけでも無いしなぁ…と。

個人的には、Taskがコールバックしたasync関数の中で await した → なんか重たい処理が走る → Taskの呼び出し元のタスクスケジューラーさんに一旦戻って、良い感じにスレッド/タスクをマネジメントしてもらってね! …ってのはアリなような気はしていて、そんな意識の元 async な関数を Callback するのもアリなんじゃないかと勝手には思っています……が、.NET Framework 側でホントにそんなステキな事やってくれるのか全くの未確認(ぉ

# 非同期関数投げるんだったら、上記コードでも Task.Run(async () => await daruiTask()) にしないとダメとか、そんなら lambda 噛まさず直接関数投げろよとか色々ツッコミ所はあるんですがまぁ…まぁ……


じゃぁ、Task で Callback する関数内は同期関数だけに絞る? …まぁ普通にアリなのではと思います。 Task は「呼び出し元が継続するのは重たすぎてブロッキングしちゃうから処理を委譲する」わけで、ブロッキングする重たい処理を好きにすればいいじゃん。みたいなー。ていうかー

ただ、微妙に気になるのは CancellationToken で要キャンセルな環境になった時ですが…

Task cookTask = Task.Run(() => CookFile(srcFile));

public void CookFile(FileInfo srcFile)
{
    byte[] dekaiBuff = Cook(srcFile);
    using(FileStream fs = new FileStream(@"\\hayai-svr\c\spool", FileMode.Create, FileAccess.Write))
    {
        fs.Write(dekaiBuff, 0, dekaiBuff.Length);
        // ↑"速い"と思ったら、LAN自体は 10Base-5 で死亡
        // 1GB を転送しようとして 2.5時間ぐらい帰ってこない!
        // ネットワーク管理者出てこいよ!!!!
        // # 一気に 1GB 書くなよって話は勘弁してください(ぉ
    }
}

↓こんな感じにすりゃ、なんか良い感じに kill ってくれる…?(書いてて無理・危険な気がしてきた…

Task cookTask = Task.Run(() => CookFile(srcFile), cancelToken);

↓こっちの方がまだお行儀良さそう…?

Task cookTask = Task.Run(() => CookFile(srcFile, cancelToken)), cancelToken);

public void CookFile(FileInfo srcFile, CancellationToken cancelToken)
{
    byte[] dekaiBuff = Cook(srcFile);
    using(FileStream fs = new FileStream(@"\\hayai-svr\c\spool", FileMode.Create, FileAccess.Write))
    {
        fs.WriteAsync(dekaiBuff, 0, dekaiBuff.Length).Wait(cancelToken);
        // ↑同期関数内だけど、非同期関数を cancelToken 付きでブロッキング
    }
}


Task わかんねーわ