ブログ移転予定です(ぜんぶ はてなの せいだ)
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 わかんねーわ
コンテナから目的の要素を抽出できなくて死亡
struct Doll { int id; std::string suffix; }; void rozen() { std::vector<int> favIdList = { 202, 707 }; // ソート済み std::vector<Doll> dolls = { // id でソート済み { 101, "dawa-" }, { 202, "kashira" }, { 303, "desu-" }, { 404, "dayo" }, { 505, "nanodawa" }, { 606, "nanoyo" }, { 707, "..." }, }; // { 202, "kashira" } と { 707, "..." } のインスタンスを抽出したい! std::vector<Doll> favDolls; }
「favIdList ∩ dolls」 的なことをしたいというか、favIdList を利用して dolls を選択だか射影したいというか、こういう時どーするのがセオリーなんですかねぇ…ってのが未だに良くわからなかったりします。
set_intersection() なんていう積集合を取ってくれるマンマな関数はあるんですけど、favIdList と dolls は型が違うので利用できないという話。仕方ないので idList を for で回し id を一つずつ抽出して equal_range() で dolls を lookup する?…といっても、やはり型が違うので無理ゲー
比較元IDだけを格納した Dolls のインスタンス作るのもナシとします
// こんなの const Dolls src = { id }; bool operator < (const Doll& lv, const Doll& rv) { return lv.id < rv.id; } // エラーチェックしろよ! const Dolls& fav = *std::equal_range(dolls.begin(), dolls.end(), src).first;
dolls を id をキーにした map 作るのもナシとします。ソート済みなのになんでソートするコンテナに入れなきゃならんのか!ダサい!
std::map<int, Doll> map; std::transform(dolls.begin(), dolls.end(), std::inserter(map, map.begin()), [](const Doll& d){ return std::move(std::make_pair(d.id, d)); }); // だからエラー(ry const Doll& fav = *map.find(favId);
こうするのがシンプルですかねぇ…。線形感がモリモリ出てダサいけど…
auto itDolls = dolls.begin(); for(int id : favIdList) { itDolls = std::find_if(itDolls, dolls.end(), [id](const Doll& d){ return id == d.id; } ); favDolls.push_back(*itDolls++); }
アルゴリズム関数やらなんやら組み合わせれば自前で創らず標準関数類だけで頑張れそうだけど、良くわからず。 umm......
Win32エラーコード一覧のページを更新しました!
Windowsのエラーコードを FormatMessage() で抽出しただけのページを更新しました! 忘れてないよ! 更新しようと思い続けてたんだよ!マジで! ちなみに前回は 2005年4月に更新していたらしので、実に12年ぶりの更新となりました。 …そんだけHP続けてるとかマジか…… そんだけ歳食ったとかマジグホァ(血
なお、次の環境で出力しています。
- Win10 1703 Creators Update
- VC2017
また、エラーコード一覧を C# / Java にも移植しましたので是非ともご利用しないでください。 C# / Java で WinAPI に用事があるようなコード書いちゃダメデショー!!
どうぞご利用ください (どっちやねん
… "死亡"してない記事書いたの久しぶりなきがするじぇ……
Visual Studio 2017 Community のインストーラーが死んで死亡
Visual Studio 2017 Community をメインPCにぶち込むぜ!ヒャッハー!! …ハイテンションwktk全裸でインストーラを実行しましたが、エラーが出てインストールできませんでした…。 が、問題が解決できたのでその報告。
条件
対策
- 服を着用します
- TSUTAYA で DOG DAYS の DVD をレンタルしてきます
- Windowsサービス "CNG Key Isolation" を起動させます。
- Visual Studio 2017 のインストーラーを起動します。
- 借りてきたDVDを鑑賞しながらインストール完了を待ちます
…というわけで "CNG Key Isolation" のサービスが起動していなかった事が原因でした。
調査方法
%tmp% に dd_bootstrapper_*.log なんてログが出力されます。 その中には次のような文言がありました。
VisualStudio Bootstrapper:2017/04/30 16:15:21: Caught Exception: Type = CryptographicException, Message = エンドポイント マッパーから使用できるエンドポイントはこれ以上ありません。
, StackTrace = 場所 System.Security.Cryptography.NCryptNative.OpenStorageProvider(String providerName)
場所 System.Security.Cryptography.CngKey.Create(CngAlgorithm algorithm, String keyName, CngKeyCreationParameters creationParameters)
場所 System.Security.Cryptography.RSACng.get_Key()
場所 System.Security.Cryptography.RSACng.Encrypt(Byte data, RSAEncryptionPadding padding)
場所 System.Security.Cryptography.RSAPKCS1KeyExchangeFormatter.CreateKeyExchange(Byte rgbData)
場所 System.Security.Cryptography.CngLightup.DetectRsaCngSupport()
あー、署名周りで死んでるねーと。
…で、ググってみたりもしたんですが、Firewall だか WindowsUpdate だか ActiveDirectory とかが引っかかってなんか違う感。 ぶっちゃけよくわからねぇ(ぉ 仕方ないのでノリでサービス周りだろうなぁという直感を元に一覧から crypt に関係するサービスを起動させてみたらなんか上手くいきました! やったねタエちゃん!! また一つ無駄に Visual Studio が増えるよ!!
VS2015 を1回起動しただけで VS2017 をぶち込んでる程度の能力