質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

4回答

5799閲覧

async/awaitでイベント待ちってできるのでしょうか?

wwbQzhMkhhgEmhU

総合スコア343

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2019/02/09 23:43

前提・実現したいこと

async/awaitなど最新?C#の技術を使ってみたいのですが、イベントを待って戻り値を返すI/Fのある部分をasync/awaitで表現できませんでした。
いろいろ考えた結果、自分では下のソースのような、Monitorを使った同期機構でしか実現できなかったのですが、もっといい方法があれば教えてください。

下のソースではMyReader.Read()が該当箇所です。Readerインターフェースを実装しており、ただただ、同期I/Oで読んだ結果を返すI/Fです。
このI/Fを実装し、外からイベントで渡されてくるデータを返したいのです。同期I/Oなのでブロックする必要があり、要求されたデータが来るまで待つ必要があります。ここをなんとかawaitで実現したいのですが、いい方法を思いつきませんでした。

なお、EventHolder/Readerの形はいじれないので、ご注意ください。
また主旨に関係ないご指摘も、気づいたことは何でも言って頂けると嬉しいです。

該当のソースコード

C#

1using System; 2using System.Collections.Generic; 3using System.Threading; 4using System.Threading.Tasks; 5 6namespace EventAsyncAwait 7{ 8 interface Reader 9 { 10 int Read(); 11 } 12 13 class MyEventArgs 14 { 15 public int val { get; set; } 16 } 17 18 class MyReader : Reader 19 { 20 List<int> buffer = new List<int>(); 21 22 public int Read() 23 { 24 int result = 0; 25 lock (buffer) 26 { 27 while (buffer.Count == 0) 28 Monitor.Wait(buffer); 29 result = buffer[0]; 30 buffer.RemoveAt(0); 31 } 32 return result; 33 } 34 35 public MyReader(EventHolder parent) 36 { 37 parent.Event += (object sender, MyEventArgs e) => 38 { 39 lock (buffer) 40 { 41 buffer.Add(e.val); 42 Monitor.PulseAll(buffer); 43 } 44 }; 45 } 46 } 47 48 class EventHolder 49 { 50 public event EventHandler<MyEventArgs> Event; 51 52 public void Fire(int v) 53 { 54 Event(this, new MyEventArgs() { val = v }); 55 } 56 } 57 58 class Program 59 { 60 static EventHolder holder = new EventHolder(); 61 62 static void Main(string[] args) 63 { 64 MyReader reader = new MyReader(holder); 65 Task.Run(async () => 66 { 67 await Task.Delay(3000); 68 holder.Fire(5); 69 }); 70 Console.WriteLine(reader.Read()); 71 } 72 } 73}

※質問しておいて何ですが、本日反応できるのは夕方くらいからになります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/02/09 23:57

コンソールアプリだから期待通りに動かないってことではありませんか? つまり、Main は非同期で実行される部分の完了を待たないで勝手に走っていって終わってしまうという。
wwbQzhMkhhgEmhU

2019/02/10 04:25

コンソールアプリとかは関係ありません。勝手に走っていってしまってるわけでもないです。 ちゃんとブロックして待っていますよ。今、3秒のディレイを入れていますが、このディレイは1分でも2分でも待っています。
pepperleaf

2019/02/10 05:03 編集

コンソールプログラムで、 Task.Run(async () => { await Task.Delay(3000); Console.WriteLine("Wake up!\n"); }); Console.WriteLine("End of Prog\n"); // ここで、プログラム終了 としたら、"Wake up!" の文字列、出ませんが、、、。 もっとも、最後に、Console.ReadKey(); で入力待ちにしたら、出た。 reader.Read(); で待っているということでしょうか。 Task.Run()の戻り値から、Taskの終了を確認するようにすべきではないでしょうか?
wwbQzhMkhhgEmhU

2019/02/10 05:03

はい、ロックしてモニタで待ってますよね?
wwbQzhMkhhgEmhU

2019/02/10 05:06

Task.Run()の戻り値からではなく、Reader.Read()の実装をしたいんですよ。。。
tamoto

2019/02/10 05:45

要件の確認をさせてください。 このイベントは「1回きり」発動することを想定したものですか?それとも、複数回発動する、「ストリーム」を想定していますか?
wwbQzhMkhhgEmhU

2019/02/10 05:55

最終的には複数回だけど、1回きりの方法すら思いついていないので、まずは1回きりでも大丈夫、という意味で、ここでは1回しか待っていません。
guest

回答4

0

他にも方法はあるでしょうが、IObservable<T> を使ってください。

Reactive Extensions を使うと簡単です。これを使うには NuGet で System.Reactive をインストールする必要があります。

追記

例えば Reactive Extensions を使ってボタンのクリックイベントを受け取るには次のようにします。

C#

1using System; 2using System.Reactive.Linq; 3using System.Windows.Forms; 4 5namespace WindowsFormsApp1 6{ 7 public partial class Form1 : Form 8 { 9 public Form1() 10 { 11 InitializeComponent(); 12 Observable 13 .FromEventPattern<EventArgs>(button1, nameof(Button.Click)) 14 .Subscribe(_ => MessageBox.Show("クリックされた!")); 15 } 16 } 17}

追記

ボタンを二つ貼り付け、button2 のイベントハンドラを次のように書き換えてください。
そして button2 を押した後、button1 を押すとメッセージが表示されます。

C#

1private async void button2_Click(object sender, EventArgs e) 2{ 3 var task = Observable 4 .FromEventPattern<EventArgs>(button1, nameof(Button.Click)) 5 .Take(1) 6 .ToTask(); 7 await task; 8 MessageBox.Show("button1 が押されました"); 9}

投稿2019/02/09 23:51

編集2019/02/10 06:10
Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

wwbQzhMkhhgEmhU

2019/02/10 04:24

すみませんが、Observer/Observableパターンでは解決しないと思います。 そもそもイベントハンドラもコールバックも、Observer/Observableパターンの一種です。
Zuishin

2019/02/10 04:27

一種ならできるのでは? 困っていることは何ですか? どうしても Task を使いたいということですか?
wwbQzhMkhhgEmhU

2019/02/10 04:32

はい、どうしてもTaskを使いたいと考えてください。 古くからある同期機構によるイベント待ちをしていますが、Taskや、それを微妙に進ませた?async/awaitに置き換えられるのか、という課題なので。
Zuishin

2019/02/10 04:35

困っていることは、同期メソッドから非同期メソッドを呼びたい、その際に必ず Task を使い、Task が終わるまでブロックしたいということで間違いありませんか?
Zuishin

2019/02/10 04:35

すべて非同期に置き換えることはできないという前提ですね?
Zuishin

2019/02/10 04:41

ちなみに、コンソールアプリから非同期を呼び出す際に、この Main は間違っているということも指摘されていますが、これはご存知の上で放置しているのですか? コンソールが関係ないというのはどういうことなのか、説明してください。
wwbQzhMkhhgEmhU

2019/02/10 04:41

同期メソッドの戻り値を返すまで、非同期イベントを待つ方法を考えたいのです。 質問に書いたとおり、ReaderとEventHolderを変えずに、同じ動きを実装したいということです。
Zuishin

2019/02/10 04:42

質問と食い違います。 非同期メソッドから同期メソッドを呼び出すことについては何の問題もありません。 ただ普通に呼ぶだけです。
wwbQzhMkhhgEmhU

2019/02/10 04:42

ん?間違っている認識はなかったです。すみません、どういうことか教えて頂けませんか?
wwbQzhMkhhgEmhU

2019/02/10 04:43

非同期メソッドから同期メソッドを呼び出すとは言っていません。。。
Zuishin

2019/02/10 04:46

同期メソッドの戻り値を返すというのは、同期メソッドを呼ぶ必要があります。 そうすると、その同期メソッドが終わるまで待つ必要があります。 その後、非同期のイベントが終了するまで待機するということで間違いありませんか?
wwbQzhMkhhgEmhU

2019/02/10 04:51

イベントは同期でも非同期でもいいんですが、同期メソッドの中で、別のメソッド?が呼ばれるまで戻り値を返すのを待ちたいということです。 ただ、質問はずっと変えてないんですが、どこがそんなに不明なのでしょう?
Zuishin

2019/02/10 04:53

それがどうして IObservable でできないのかわからないからです。 質問の意図が伝わっていない可能性があるので聞いています。
wwbQzhMkhhgEmhU

2019/02/10 05:01

ReaderのReadメソッドを実装したいんですよ。どうやったら出来るのか教えてください。
Zuishin

2019/02/10 05:06 編集

どう言えば通じるんだろう。
Zuishin

2019/02/10 05:06

わからないことがあるので、質問をしています。 答えがないのでわかりません。 Read メソッドは実装済みで変更できないんでしょう? イベントをシーケンシャルに処理したいなら全部非同期にして IObservable で処理するのが簡単です。 同期メソッドを止めながらブロックしないという意味がよくわかりません。 止めるのとブロックするのは同義です。
Zuishin

2019/02/10 05:07

async/await は非同期で使うものです。 これはよろしいですか?
Zuishin

2019/02/10 05:08

止めたいだけなら Task.Wait が使えますが、これも良いんですよね?
wwbQzhMkhhgEmhU

2019/02/10 05:10

> Read メソッドは実装済みで変更できないんでしょう? はい、このI/Fは変更できません。始めからそう書いています。 > async/await は非同期で使うものです。 > これはよろしいですか? 別にどこで使ってもいいと思いますが、awaitを使うなら、asyncでないとダメですね。
wwbQzhMkhhgEmhU

2019/02/10 05:11

> 止めたいだけなら Task.Wait が使えますが、これも良いんですよね? はい
Zuishin

2019/02/10 05:13

どうして肝心なところがいつも無視されるんでしょうか。 ひとつづつ聞いた方がいいんでしょうかね。 一つ目。 全部非同期にすることはできませんか?
wwbQzhMkhhgEmhU

2019/02/10 05:17

?には回答しましたし、無視は一度もしてないつもりですよ。 Readerの実装が出来るのなら。できるんですか? あなたの言う同期・非同期の意味合いが正確ではありません。
Zuishin

2019/02/10 05:18 編集

同期メソッドを止める場合には必ずメインスレッドがそこで止まります。 メインスレッドを止めてはいけないのなら、同期メソッドを止めてはいけません。 つまり、非同期メソッドの戻り値をそこで待ってはいけません。 この解決策としては、Task を使った非同期メソッドに置き換えるか、メインスレッド以外を待たせるかということになります。 これが「全部非同期にする」ということです。 これが受け入れられないということでいいんでしょうか?
Zuishin

2019/02/10 05:24

一応引用しておきます。 > ReaderのReadメソッドを実装したいんですよ。どうやったら出来るのか教えてください。 これは撤回ということですよね?
wwbQzhMkhhgEmhU

2019/02/10 05:27

あなたの文章の間違い探しをしているわけではないです。 私はawait処理をC言語のsetjmp/longjmpのようなものと捉えています。 そういう意味では、asyncでないという意味での同期メソッドをTask.Wait()するなら、必ずしもメインスレッドが止まるわけでもないです。Waitしてる間、メインスレッドで動作できます。
wwbQzhMkhhgEmhU

2019/02/10 05:28

また別スレッドでないという仮定もしていません
wwbQzhMkhhgEmhU

2019/02/10 05:29

> これは撤回ということですよね? 撤回したことはありませんよ
Zuishin

2019/02/10 05:29

では Task.Wait を使えばいいのでは?
wwbQzhMkhhgEmhU

2019/02/10 05:33

だからどうやればできるの?って聞いています。
Zuishin

2019/02/10 05:36

Task.Wait でイベントを待つことができれば解決しますか? イベントが来るまでずっと止まりますが、それができれば解決ということでいいですか?
wwbQzhMkhhgEmhU

2019/02/10 05:37

始めからそう言ってるつもりですけど。。。 具体的には、NAudioのWasapiLoopbackCaptureというクラスを使って、デスクトップの音を拾っています。 これをネットワーク越しに送りたいのですが、この人はStreamのI/Fを持っていないので、そこを実装してあげようとしているのです。ただ、WasapiLoopbackCaptureは、イベントでデータを送ってくるので、そこをReadで待ってあげないといけません。そこを可能であればTask/async/awaitで実装したいのです。
Zuishin

2019/02/10 05:39

最初から言っていることがわからないので聞いています。 イエスかノーで答えてください。
wwbQzhMkhhgEmhU

2019/02/10 05:47

> Task.Wait でイベントを待つことができれば解決しますか? イベントで渡される引数をRead側でもらえれば、Task.Wait でイベントを待つことで解決します > イベントが来るまでずっと止まりますが、それができれば解決ということでいいですか? イベントで渡される引数をもらえれば、止まっても大丈夫です。
Zuishin

2019/02/10 05:52

イベントは回答に示したように IObservable<EventArgs> に変換できます。 そして IObservable は ToTask() で Task に変換できます。 これを Wait してください。 ふつうはそこで止まりますが。
Zuishin

2019/02/10 05:54

ToTask の戻り値は Task<System.Reactive.EventPattern<EventArgs>> です。
tamoto

2019/02/10 05:54

横からすみません。 質問者さんへ。「EventHolder/Readerの形はいじれない」とおっしゃっていますが、現状の同期Read(`int Read();`)と、非同期Read(`Task<int> ReadAsync();`)はどうあがいても異なるAPIになってしまうため、その要件を満たしつつ非同期にするのは「不可能」なのです。という話をしています。現状では`Read()`を呼んだときに`int`を返す必要があり、ここで結果を同期待ちすることが必須となるため、非同期化のしようがありません。
wwbQzhMkhhgEmhU

2019/02/10 05:56

だから、Task.Wait()でいいという話まで、Zuishinさんにしていたところです。。。 非同期メソッドかどうかというところは重要ではありません。
Zuishin

2019/02/10 05:58

ではこれで解決しましたね? 低評価を取り消して解決済みにしてください。
tamoto

2019/02/10 05:59

はい、Task.Wait()を使うということは、非同期化した上で同期化する、ということになるので、何も非同期になっていないし実行速度は落ちるし何の意味もないのですが、それをやりたいということですか?
wwbQzhMkhhgEmhU

2019/02/10 06:01

> ではこれで解決しましたね? 何が解決したんでしょう?ごめんなさい、意味が分からないです。
Zuishin

2019/02/10 06:02

え? これで Task.Wait できるでしょ?
wwbQzhMkhhgEmhU

2019/02/10 06:02

> はい、Task.Wait()を使うということは、非同期化した上で同期化する、ということになるので、何も非同期になっていないし実行速度は落ちるし何の意味もないのですが、それをやりたいということですか? はい。 別に非同期I/Oを呼び出して、同期I/Oを実装、は普通に行われると思いますが。。。
Zuishin

2019/02/10 06:03

もう一度書きましょうか? イベントは回答に示したように IObservable<EventArgs> に変換できます。 そして IObservable は ToTask() で Task に変換できます。 これを Wait してください。
wwbQzhMkhhgEmhU

2019/02/10 06:03

> これで Task.Wait できるでしょ? それでイベントを待つ方法を思いつかなかったんですよ。。。
Zuishin

2019/02/10 06:05

> イベントは回答に示したように IObservable<EventArgs> に変換できます。 > そして IObservable は ToTask() で Task に変換できます。 私の回答を読んでください。 どこにも難しいことは書いていないと思いますが。 イベントが Task に変換できればいいのでしょう?
wwbQzhMkhhgEmhU

2019/02/10 06:07

これ通知する側の機能ではない?こちらはIObserver側ですよ
tamoto

2019/02/10 06:08

非同期I/Oを同期的に呼び出すは普通には行われません……それは非同期を理解していない人が書いた危険で非効率なコードという意味です……
wwbQzhMkhhgEmhU

2019/02/10 06:10

非同期I/Oをラップする形で同期I/Oを書いたらそうなるんですね。。。 悲しいです。
Zuishin

2019/02/10 06:10

追記しました。これでさすがにいいでしょう?
Zuishin

2019/02/10 06:11

> 悲しいです。 いまさら? とにかく質問には完ぺきに答えました。 聞く耳持たなかったのはあなたです。 低評価を取り消してベストアンサーにしてください。
tamoto

2019/02/10 06:13

勝手に補足ですが、 Zuishinさんの追記のコードは、「Eventの発動を非同期Task化する」というまさに要件通りのもので、それを`Read()`内で呼び出しTask.Wait()することで質問者さんの意図通りの動作をする、というもので、それにSystem.Reactiveの機能を利用しているだけです。最も合理的に書かれています。要件自体が非合理なのを置いといてですが。
wwbQzhMkhhgEmhU

2019/02/10 06:19

実際に試してはいませんが、要件は実現できてそうですね。 聞く耳は持っていて、誠実に対応したつもりですよ。質問の回答にはなっていないですけどね。 これから確認をしますので、少々お待ちを。
Zuishin

2019/02/10 06:22

質問がどういうものなのかわからなかったのでここで聞いて最終的にどうなれば解決なのかをはっきりさせました。 だから質問の回答になっています。 取り消してください。
wwbQzhMkhhgEmhU

2019/02/10 06:30

すみませんが、MyReaderクラスの書き換えでないと回答ではありません。
Zuishin

2019/02/10 06:31

あなたの MyReader クラスは動きません。 動くと主張しているのはあなただけです。
Zuishin

2019/02/10 06:33

> > Task.Wait でイベントを待つことができれば解決しますか? > イベントで渡される引数をRead側でもらえれば、Task.Wait でイベントを待つことで解決します > > イベントが来るまでずっと止まりますが、それができれば解決ということでいいですか? > イベントで渡される引数をもらえれば、止まっても大丈夫です。 止まっても大丈夫ということをはっきり書いています。
Zuishin

2019/02/10 06:35

私はあなたの要件を細かく確認し、完璧な回答を用意しました。 まさに要件通りのものがここにあります。 あとは「大丈夫」と言ったあなた自身が解決すべきことです。 私はそれで本当に大丈夫なのか何度もちゃんと確かめました。
Zuishin

2019/02/10 06:39

> あなたの文章の間違い探しをしているわけではないです。 > 私はawait処理をC言語のsetjmp/longjmpのようなものと捉えています。 > そういう意味では、asyncでないという意味での同期メソッドをTask.Wait()するなら、必ずしもメインスレッドが止まるわけでもないです。Waitしてる間、メインスレッドで動作できます。 この通り、動作することを保証しているのはあなたであり、私ではありません。 そして私はちゃんと代替え案を二つ用意していますが、あなたはそれを無視してこの仕様に落ち着きました。ですからこの質問に対する回答はこれで間違いありません。 私はあなたのソフトに完成まで付き合う気はありませんので、一つの質問の解決をもって解決としてください。 それができなければあなたは Q&A サイトを使うことはできません。
wwbQzhMkhhgEmhU

2019/02/10 06:40

質問の意図が分からないと言われて、それに答えると、求めている回答が変わるわけではありませんよ。 いずれにせよ、確認は普通にするのでご安心ください。
wwbQzhMkhhgEmhU

2019/02/10 06:41

ん?awaitでデッドロックするってこと?
Zuishin

2019/02/10 06:41

イベントを Task に変換し、それが Wait できれば解決ということになっているはずなので、勝手に拡大しないでください。
wwbQzhMkhhgEmhU

2019/02/10 06:44

まぁあなたはそれでいいのでしょうね。 残念ですが、QAサイトとしてはそれではいけないと思います。
Zuishin

2019/02/10 06:47

だめだこりゃ。
tamoto

2019/02/10 06:48

> Zuishinさん Taskを経由して同期待ちするだけのMyReaderの実装を書いてあげるのが良いと思います。自分が書くのは申し訳ないので。動くことには動きます。完全同期で。
Zuishin

2019/02/10 06:50

tamoto さん、二種類書いていますが破棄します。
tamoto

2019/02/10 06:51

質問者さんへ。QAサイトは困り事を解決する場ですが、最初から不合理な要件を見かけたら、「それはおかしい」と声を上げるのもQAサイトの役割です。要件をそのまま回答することを求めるなら、それは外注に依頼するべきものです。 例えばですが、「データベースに1万回アクセスしてから1件のデータを取りたい」っていう要件を聞いたとして、「1万回アクセスしなくてもよくない?」って声を上げるのはおかしいでしょうか?
wwbQzhMkhhgEmhU

2019/02/10 06:52

要件は別におかしくなかったでしょ?
wwbQzhMkhhgEmhU

2019/02/10 06:52

同期I/Oと非同期I/Oはどちらも需要はありますよ。
wwbQzhMkhhgEmhU

2019/02/10 06:53

同じ実装にするためにも、非同期I/Oで同期I/Oを実装するのは普通だと思います。
tamoto

2019/02/10 06:55

最初から「要件がおかしい」という話をしています。 同期IOを同期IOとして使う、非同期IOを非同期IOとして使う、はなにもおかしくありません。 その上で、「同期IOを非同期IOに書き換える」という話ならこちらも回答できましたが、「同期IOを非同期IOを経由して同期IOとして使う」はなんだそりゃ、っていう話です。
wwbQzhMkhhgEmhU

2019/02/10 07:08

同期I/Oは通常、非同期I/Oをwaitして作りますよ。そうでないと実装がバラバラになります。 async/awaitの話ではないです。.NETの話ではないので。。。 まぁなんでも同じなんですけど。
Zuishin

2019/02/10 07:12

じゃあ Wait すれば? そこから先は他人の責任ではありません。 自分で言い張ってることの責任は自分でとってくださいな。
tamoto

2019/02/10 07:17

Zuishinさんの追記のコードを参考に、ToTask()でEventからTaskを作ってください。そのTaskをWait()してください。それらをRead()の中に書いてください。これで要件を満たせますでしょうか。
wwbQzhMkhhgEmhU

2019/02/10 07:28

Zuishinさんへ まだちゃんと仕組みが理解できてないので、何とも言えませんが、やれることはやるので心配の必要はありません。ただ、責任とかとは関係ないですよ。「要件が分からない」と言った人に説明をしただけなので。 まぁ動く方法が見つかれば、そこからアプローチはできるはずなので、お気になさらず。 tamotoさんへ 今諸々の周辺技術の記事を読んでるところです。まだ仕組みがきちんと理解できていないので。 この辺だけでもFromEventとFromEventPatternの違いがあるようです。 できればその実装の中身も知りたいですね。なるべく急ぎますが、先は長いです。
Zuishin

2019/02/10 07:30

心配の必要がないなら解決済みにしてください。 私にベストアンサーを与えたくないなら自己解決でどうぞ。
Zuishin

2019/02/10 07:34

まあこのほとんどそのままのコードを見てこれだけ時間がかかるってことは「解決できない」っていうことなんでしょうけどね。
wwbQzhMkhhgEmhU

2019/02/10 07:42

どちらもまだ確認中なので、何とも言えません。 別にそんなすぐにはできませんよ?ここでチャットしてる時間の方が長いです。
Zuishin

2019/02/10 07:44

すぐにできますよ。 あなたが遅いだけです。
wwbQzhMkhhgEmhU

2019/02/10 07:47

そういう感覚は人それぞれだと思います。説明が大変ですね。 でもこれReflection使ってるのかなぁ・・・(独り言)
Zuishin

2019/02/10 07:51

リフレクションは使ってるにきまってるでしょう。 nameof 演算子を見れば一目瞭然です。 使わない方のオーバーロードはあなたに理解しにくいだろうから簡単な方を使ったんですよ。
Zuishin

2019/02/10 07:53

まさかこちらまで理解できないとまでは思いませんでしたが、これ以上簡単なオーバーロードってないんですよね。
wwbQzhMkhhgEmhU

2019/02/10 07:58

いやぁ十分難しいと思いますけど・・・ いずれにしてもすぐできそうには思えないです
Zuishin

2019/02/10 08:00

いやいやご謙遜を。 さすがに中学生以下ということはないでしょう。
Zuishin

2019/02/10 08:19

それで低評価できるはずがないじゃないですか。 わからないのに今まで偉そうに上から目線で語ってたなんてそんな面白いこと。
Zuishin

2019/02/10 08:22

ちょっと語尾にドヤァって入れてみますね。 > すみませんが、Observer/Observableパターンでは解決しないと思います。(ドヤァ > そもそもイベントハンドラもコールバックも、Observer/Observableパターンの一種です。(ドヤァ
wwbQzhMkhhgEmhU

2019/02/10 08:38

いや.NETにReactiveなんて思想があると思ってなかったですw Observerに反応して何コレ?大丈夫?と思ってすぐ低評価しましたw でもほんとコレ難しいw どういう仕組み?w
Zuishin

2019/02/10 08:41

理解できたならまずは低評価を外しましょう。間違ってもいないのに低評価されるいわれはありません。
wwbQzhMkhhgEmhU

2019/02/10 08:49

そこは取り敢えずデッドロック回避の方法まで検討できたからかなぁ。 Taskをキューイングして取るくらいにしないと出来なそう。 まず仕組みが分かってないので。。。
Zuishin

2019/02/10 08:51 編集

デッドロック回避の方法は教えました。現に私のサンプルコードはデッドロックしないはずです。
wwbQzhMkhhgEmhU

2019/02/10 09:03

ん?俺のコードだとしちゃったんだがw 何が違うんだろw 事前にイベント発行してるとアウトなので、ロックの中で待ったら当然デッドロックと。。。 俺の方法ではデータの渡し方にロックが必要な方法を取っている以上、こういうのはどうしても起きるので、もうちょっとReactiveな仕組みを調べて回避しないと…
Zuishin

2019/02/10 09:11

> Task.Wait でイベントを待つことができれば解決しますか? > イベントが来るまでずっと止まりますが、それができれば解決ということでいいですか? > 始めからそう言ってるつもりですけど。。。(ドヤァ Task.Wait でイベントを待つことができればデッドロックしても解決だって最初から言ってたそうですが。 私は最初からあなたのコードそのままだとデッドロックするけどそれでいいのかって言ってるでしょ。 tamoto さんも。
wwbQzhMkhhgEmhU

2019/02/10 09:17

イベントが来ても止まってるでしょw デッドロックしてもいいとは一度も言ってないよ Monitorの代わりを作るか、別のところで同期を取って何らかの方法で受け渡すしかない。
Zuishin

2019/02/10 09:22

イベントが来てないから止まってるんですよ。
wwbQzhMkhhgEmhU

2019/02/10 09:33

デッドロックしてるのはリストをlockしてるところなので、イベント自体は来てますよ この中とTask.WaitするとMonitor.Waitのようには待てないのでデッドロックするということです
Zuishin

2019/02/10 09:47

> そういう意味では、asyncでないという意味での同期メソッドをTask.Wait()するなら、必ずしもメインスレッドが止まるわけでもないです。Waitしてる間、メインスレッドで動作できます。 なるほど、イベントは来てるし止まってないのにデッドロックすると。
wwbQzhMkhhgEmhU

2019/02/10 10:02

通常はしないでしょw しょうもない揚げ足取りしないw スレッドが違うかもしれないからリソースの同期をした結果、デッドロックを起こすってだけで、awaitとは関係ないよ。 ただ、現状の解からは俺には回避策が分かっておらず、まだ回答には至っていないだけ。 コンストラクタで起動したタスクの結果を綺麗にReadで受け取れればいいんだけど、、、
Zuishin

2019/02/10 10:06

で、既に解決策は教えているはずですが、これ以上何を言えと? コード全部私が書けと? あなたが書く以上、つたない部分があってもあなたのせいでしょう。
Zuishin

2019/02/10 10:08

私が「それじゃ動かない」といくら言ってもあなたが「これで動く」とマウントポジションで言ったんだから。
wwbQzhMkhhgEmhU

2019/02/10 10:11

いや、私は今確認をしてる最中なだけで何も求めてないですよ。。。 あなたが解決済みとベストアンサーを要求してて、俺はまだそういう段階ではないと言ってて揉めているだけ。急かさず心配しないで待っててくれ、とは言ってるけども、矢継ぎ早にレスが来るので回答に追われてる状態です。
Zuishin

2019/02/10 10:14

優先すべきことは何か今一度よく考えましょう。
wwbQzhMkhhgEmhU

2019/02/10 10:14

> 私が「それじゃ動かない」といくら言ってもあなたが「これで動く」とマウントポジションで言ったんだから。 そんなことはしてないですw
Zuishin

2019/02/10 10:16

> そういう意味では、asyncでないという意味での同期メソッドをTask.Wait()するなら、必ずしもメインスレッドが止まるわけでもないです。Waitしてる間、メインスレッドで動作できます。
wwbQzhMkhhgEmhU

2019/02/10 10:18

これは事実ですよね? どの辺でマウントしてるんでしょw
Zuishin

2019/02/10 10:18

まあマウントポジションは私の脳内のあなたがしてたんですけどね。 草を生やしながら私に間違った情報をご教授してくれてるところを見て想像しました。
Zuishin

2019/02/10 10:18

事実ではありません。
Zuishin

2019/02/10 10:19

現に動かないでしょ? 何を言っているんですか?
wwbQzhMkhhgEmhU

2019/02/10 10:23

UIスレッドはどうやって動いているんでしょうね。awaitしてる間、別のことしてますよね? あなたの理屈だとUIスレッドはawaitしてる間、終わるまで何もできなくなりますよ?
Zuishin

2019/02/10 10:26

await と Task.Wait が同じものだと思っていたのですか。
wwbQzhMkhhgEmhU

2019/02/10 10:28

え?ほぼ同じじゃないの?違うの? だから話が噛み合わなかったのか
wwbQzhMkhhgEmhU

2019/02/10 10:28

asyncとかおまけだと思ってました
Zuishin

2019/02/10 10:30

ほぼ同じ物を新しく作る意味などないでしょう。ほぼ同じ物があれほど話題になったわけもないでしょう。 全然違います。
wwbQzhMkhhgEmhU

2019/02/10 10:51

これはやばそうだね。。。 実装する前にしっかり調べておかないといけなそう。まずはそっち調べるわw
wwbQzhMkhhgEmhU

2019/02/10 12:31

調べれば調べるほど、いろんな記事と違う現象が出てきて、よく分からない。 現状だとデッドロックするという現象は出てないし、Task.Runの直前直後にWaitを入れると、Task.Runの前後と中が全て同一のスレッドで実行される。 よく分からず、追うべきソースもないので、一旦ここでは棚上げにしてもいい?
wwbQzhMkhhgEmhU

2019/02/10 21:22

■Task.Wait()のデッドロック問題について 現状だと、Task.Waitと同じスレッドでTask.Runの中のスレッドが動作していたりすることもあり、Wait中にスレッドが使用されている形跡がある。 また、Task.Waitをawaitで待ち合わせるとデッドロックするとの記事があったが、その記事通りの実装、かつ同一スレッドでの待ち合わせで固まっていなかった。 なぜawaitと違う挙動という言及がされているのか、よくわからない。勘違いかもしれないので、とりあえず棚上げ。 ■System.Reactive拡張を使用したeventの待ち合わせについて MyReader.ReadにてSystem.Reactive拡張を使用したeventの待ち合わせを実装してみたところ、いくつか問題があった。 (1)そのままの形で使用するとなると、単純にタスクに変換してWaitすることになるが、その際にデータの取得が出来ず、イベントでの呼び出しとスレッドが違う可能性があるため、ロックが必要になる。ロックが必要になると、Monitor.Waitのような機構がないため、そのままではデッドロックしてしまう。せいぜいTask.Wait直前/直後にロックを分けることで、回避することになるが、直前のロック中にイベントが発生した場合、ちゃんとイベントが拾えるとは限らないコードになった。 (2)(1)のデータ同期問題をクリアするため、Subscribeして、eventの待ち合わせを実装してみたが、Readメソッド内でeventが発生しないケースで、イベントの取りこぼしが起きた。原因はSubscribeをReadメソッド内でしていたからだが、Subscribeをコンストラクタ側ですると、Readメソッド外でイベントが発生してしまい、意味がなかった。回避手段としては、ReplaySubjectの使用を検討したが、これもConnectが一度しか出来ず、一度Connectすると全部流れてしまうので、最初の1つだけ処理して、他は全部残す的な使い方ができなかった。Subjectに逐一Replayしたとしても、結局1つずつ取得する手段はないので、どこかに貯める必要があると判断。こうなるとデータ同期問題を回避できないので、この方法は自分の現状の知識では実現不可と判断した。 結果、async/await/Taskでイベントを待つには、少なくとも力不足、と考える。 現実的には、現状の実力ではMonitorで同期する方が、簡潔に書ける、と思う。 Rx大好きな人なら美しく何とかしちゃう可能性もある、とも思うw 懸念点は、Task.Waitを使っている点。棚上げ中となっている。 (1)については、ソースが気になる人がいるかもなので、以下が現実装。 残念ながら、同期が不完全でNGです。 using System; using System.Collections.Generic; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading.Tasks; namespace EventAsyncAwait { interface Reader { int Read(); } class MyEventArgs { public int val { get; set; } } class MyReader : Reader { List<int> buffer = new List<int>(); public int Read() { int result = 0; bool needWait; lock (buffer) { needWait = (buffer.Count == 0); } if (needWait) { Task task = Observable.FromEventPattern<MyEventArgs>( h => _parent.Event += h, h => _parent.Event -= h) .Take(1) .ToTask(); task.Wait(); } lock (buffer) { result = buffer[0]; buffer.RemoveAt(0); } return result; } EventHolder _parent; public MyReader(EventHolder parent) { _parent = parent; parent.Event += (object sender, MyEventArgs e) => { lock (buffer) { buffer.Add(e.val); } }; } } class EventHolder { public event EventHandler<MyEventArgs> Event; public void Fire(int v) { Event(this, new MyEventArgs() { val = v }); } } class Program { static EventHolder holder = new EventHolder(); static void Main(string[] args) { MyReader reader = new MyReader(holder); Task.Run(async () => { await Task.Delay(3000); holder.Fire(5); }); Console.WriteLine(reader.Read()); } } }
wwbQzhMkhhgEmhU

2019/02/10 21:35

追伸です。 (1)でリストをスレッドセーフにするという手もありますが、いずれにしてもWaitの境界で通知の有無が微妙なので、同じですよ。念の為。
Zuishin

2019/02/11 04:57

この程度の物は一人で作れるのでご教授いただかなくて大丈夫です。二種類作って捨てました。
wwbQzhMkhhgEmhU

2019/02/11 05:13

いや、証拠もなくNGにできないので、出しただけですよ あと「ご教授」ではなく「ご教示」ですって昔言ってる人がいたのを思い出しました
Zuishin

2019/02/11 05:21

何の証拠か知りませんが「あなたが」できないのは私のせいではありません。 私はできましたから。
wwbQzhMkhhgEmhU

2019/02/11 05:30

別にあなたが正解を持っていなくても、別に誰かが困るわけでもないので、気にもとめませんよ。 ただ、できた証拠がない、もしくはどこがあなたのできたものと違っていて、確実に出来ていたという証明がない限り、このQAサイトでいえば、残念ですが、「あなたも」できていないと考えざるを得ません。 これで終わりにしましょうかね。
Zuishin

2019/02/11 05:36

自己解決をどうぞ。
Zuishin

2019/02/11 05:38

できないという結論でどうぞ。
Zuishin

2019/02/11 05:41

これだけのヒントをあげたのに。ほぼ答えじゃんというのが私の感想です。
wwbQzhMkhhgEmhU

2019/02/11 05:55

現状の結論ではできないという結果であり、それは悪魔の証明になるので回答にはならないです。 Rxのライブラリに自作の機能を追加か、現状のイベントハンドラと同等の同期を入れれば出来る方法があるのかもしれませんが、残念ですがZuishinさんの方法では不可能です。 .NETのRx系統のライブラリの存在は知らなかったので、個人的に得られるものはありましたが、質問に対する回答としては、そうなります。ヒントには気付きませんでしたが、Zuishinさん文句が多いし、言い掛かりばかりで証拠の提示がないのがほとんどでしたね。次回があるなら、もう少し文量に見合った、技術的にまともなやり取りができればいいなと思っています。 というわけで、すみませんが、このまま放置になります。私もやることがあるので、そろそろいいですよね。
Zuishin

2019/02/11 05:58

質問を放置せずきちんと解決してください。
Zuishin

2019/02/11 06:43

ご教授いただかなくても悪魔の証明くらい知ってます。あなたにとっては最近覚えた言葉かもしれませんが、一般常識です。 数分で解決可能な問題を質問サイトを使ってまでこれだけ時間と労力を使って解決できないというのは異常ですね。質問者が他の人なら悪魔の証明を持ち出すまでもなくすぐに解決したことでしょう。 解決できる回答者が回答欄にもコメント欄にも修正依頼欄にもこんなにいるのになぜ解決できなかったかわかりませんか? これだけ時間をかけて何も得られないあなたは果たして自分の思うほど有能なんですかね?
wwbQzhMkhhgEmhU

2019/02/11 06:50

現状事実の積み上げで解決できていないのですよ。。。説明何度もしましたよね? 悪魔の証明を知っているなら、続ける意味がありません。 NGにされたからって、いい加減言い掛かりばかり言うのはやめませんか? 何の話をしてるのでしょう?大丈夫ですか?
Zuishin

2019/02/11 06:55

続ける意味がないならやめたらいいのでは? 大丈夫ですか?
Zuishin

2019/02/11 06:56

数分で解決可能な問題を質問サイトを使ってまでこれだけ時間と労力を使って解決できないというのは異常ですね。質問者が他の人なら悪魔の証明を持ち出すまでもなくすぐに解決したことでしょう。
wwbQzhMkhhgEmhU

2019/02/11 07:05

ここ基本質問者の返事で終わる欄なので、申し訳程度に付き合ってあげてるだけですよ。 同じことを繰り返してまで、言うことなんですかね。 反証もないあなたの脳内の結論に興味はないんですよ。この辺で終わりにしませんか?
Zuishin

2019/02/11 07:13

なんで続けてるんですか? 大丈夫ですか?
Zuishin

2019/02/11 07:14

これだけ時間をかけて何も得られないあなたは果たして自分の思うほど有能なんですかね?
wwbQzhMkhhgEmhU

2019/02/11 07:17

ちょw 何これw どうしたの? 俺が有能かどうかなんてどうでもいいよw 構ってほしいの?
Zuishin

2019/02/11 07:19

え? いまさら?
wwbQzhMkhhgEmhU

2019/02/11 07:22

マジで構ってほしいだけなら、こんなとこ使わないでw チャットがしたいなら行けるとこなら行ってあげてもいいし。。。 まぁそんなに頻繁にはできんと思うけど OK?
Zuishin

2019/02/11 07:25 編集

そんなこと言わず構ってくださいよ。私、タフでプライドの高い無能大好きなんです。あなたがそうならいいなと思ってます。
wwbQzhMkhhgEmhU

2019/02/11 07:27

ちょっとさすがにこの場でこれはいかん。通報しといた。
Zuishin

2019/02/11 07:32

残念。タフじゃなかった。ただのプライドの高
wwbQzhMkhhgEmhU

2019/02/11 07:36

いや別にココでなければ行けるとこなら行くって言ってるんだけども。。。 仕方なくね? プライドなんてミジンコ並なんだが
Zuishin

2019/02/11 07:40

残念。プライドもミジンコだった。ただの無
Zuishin

2019/02/11 07:42

まあそれなら用はないんでつきまとわないでください。
wwbQzhMkhhgEmhU

2019/02/11 07:44

期待に添えなくて悪いね じゃあこれで終わりってことで
Zuishin

2019/02/11 07:45

気持ち悪いんで、はなしかけないで。
guest

0

こんにちは。

そのMain関数を別関数にしてasync関数とし、Task.Runの中でReadを呼び出し、そのTask.Runをawaitすれば良いはずです。int ret=await Task<int>.Run(()=>{return reader.Read();});のようなイメージです。

async関数を呼び出すと、awaitにてTaskを起動したら直ぐにasync関数の呼び出し元に戻ってきます。
Task.Runはサブ・スレッドで実行を開始しReadが終わればそのままawaitの次から実行を継続します。
aync関数から戻った地点と、awaitの次の地点がそれぞれ異なるスレッドで実行されています。これらを同期させる仕組みも何か必要と思います。

以上の動作はコンソール・アプリの場合です。GUIアプリの場合も見た目には良く似た動作をしますが、実際の処理内容はかなり異なります。最大の差はawaitの次をメッセージ・ループを回しているスレッド(通常はメイン・スレッド)で実行することです。

投稿2019/02/10 08:36

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

wwbQzhMkhhgEmhU

2019/02/10 09:21

う〜ん、それではReadでどう待つと言っているのでしょうか? 根本的なところが違っているような…
Chironian

2019/02/10 11:26

提示されているRead()関数は同期的にイベント発生を待っていると思います。そのままで良いですよ。 同期関数をTask.Runで呼び出して非同期化するという、async/awaitの定番的な使い方の1つです。 https://chronoir.net/cs-async-await-taskrun/
Chironian

2019/02/10 11:27

ああ、ごめんなさい。Zuishinさんとのやりとりを見ていませんでした。 私はこれにて降りさせて頂きます。
wwbQzhMkhhgEmhU

2019/02/10 13:31

一応書いておくと、そのままで何か不都合のある動作をしているわけではありません。 MyReaderクラスのReadメソッドの中身をasync/await/Taskで実装できないかを検討したく、呼び出し方法部分は検討対象ではない説明用の部品なので、申し訳ありませんが、このままにします。
guest

0

自己解決

さらにSystem.Reactive拡張を読んでみたのですが、肝心の待ち合わせの部分のソースを見つけられなくてできませんでした。恐らく、async/await/Task+言語仕様だけで待ち合わせを実現したのではなく、何らかの同期機構をセットで使用したのではないか?と推測しています。

そこで、今回はMonitorを使用して待ち合わせた実装を、もう一つの回答で書いたものと同様な、汎用なイベントハンドラをラップする実装に組み込んでみました。ようはSystem.Reactive拡張を真似て、Monitorを使う待ち合わせを使ったasync/awaitの簡易版汎用実装をしてみたということです。なるべくメモリ効率を良くして、コンテキストスイッチを抑える実装にはしてみたつもりです。

なお、前回同様エラーハンドリングはないので、そのまま使っちゃダメですよ。
これを数日様子見して解決の判断をするつもりです。

C#

1using System; 2using System.Collections.Generic; 3using System.Threading; 4using System.Threading.Tasks; 5 6namespace EventAsyncAwait 7{ 8 interface Reader 9 { 10 int Read(); 11 } 12 13 class MyEventArgs 14 { 15 public int val { get; set; } 16 } 17 18 class EventAwaitableFunction<A> 19 { 20 public class EventContext 21 { 22 public object Sender { get; set; } 23 public A Args { get; set; } 24 } 25 26 public delegate void AddHandler(EventHandler<A> h); 27 private List<EventContext> buffer = new List<EventContext>(); 28 29 public EventAwaitableFunction(AddHandler add) 30 { 31 add((s, a) => 32 { 33 var elm = new EventContext(); 34 elm.Sender = s; 35 elm.Args = a; 36 lock (buffer) 37 { 38 buffer.Add(elm); 39 Monitor.PulseAll(buffer); 40 } 41 }); 42 } 43 44 public async Task<EventContext> GetFirstEvent() 45 { 46 EventContext result = null; 47 lock (buffer) 48 { 49 if (buffer.Count > 0) 50 { 51 result = buffer[0]; 52 buffer.RemoveAt(0); 53 } 54 } 55 if (result == null) 56 { 57 await Task.Run(() => 58 { 59 lock (buffer) 60 { 61 while (buffer.Count == 0) 62 { 63 Monitor.Wait(buffer); 64 } 65 result = buffer[0]; 66 buffer.RemoveAt(0); 67 } 68 }); 69 } 70 return result; 71 } 72 } 73 74 class MyReader : Reader 75 { 76 EventAwaitableFunction<MyEventArgs> func; 77 List<int> buffer = new List<int>(); 78 79 public int Read() 80 { 81 return ReadAsync().Result.Args.val; 82 } 83 84 public async Task<EventAwaitableFunction<MyEventArgs>.EventContext> ReadAsync() 85 { 86 return await func.GetFirstEvent(); 87 } 88 89 public MyReader(EventHolder parent) 90 { 91 func = new EventAwaitableFunction<MyEventArgs>(h => parent.Event += h); 92 } 93 } 94 95 class EventHolder 96 { 97 public event EventHandler<MyEventArgs> Event; 98 99 public void Fire(int v) 100 { 101 Event(this, new MyEventArgs() { val = v }); 102 } 103 } 104 105 class Program 106 { 107 static EventHolder holder = new EventHolder(); 108 109 static void Main(string[] args) 110 { 111 MyReader reader = new MyReader(holder); 112 Task.Run(async () => 113 { 114 await Task.Delay(3000); 115 holder.Fire(5); 116 }); 117 Console.WriteLine(reader.Read()); 118 } 119 } 120}

追記)
SynchronizationContextによっては、実行スレッドによりawait後のスレッドに制約がかかるため、Task.Waitと競合してデッドロックすることがあります。
コンソールアプリ以外で使用するケースでは、そのようなケースが起こりうるので、ご注意ください。
例えば、これ100msくらいで終わるからUIスレッドで実行しよう、とかすると、100ms経過しても返ってこず、固まるということです。確実にUIスレッド以外から実施してください。ASP.NETでの回避方法は未確認です。
ConfigureAwaitによる回避は↓の記事にもある通りやめるべきだと思います。

デッドロックについては、↓が詳しいです。
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

SynchronizationContextについては、↓参照です。
https://msdn.microsoft.com/magazine/gg598924.aspx

最後に、納得の行かない人もいるかもしれないので、書いておきますが、待ち合わせという性格上、このI/Fをawaitで実装する限り、Task.Waitは仕方のない選択です。有効なケースであれば問題はないので、個人的に分かって使う分には益がある、と考えています。

投稿2019/02/11 21:19

編集2019/02/13 04:44
wwbQzhMkhhgEmhU

総合スコア343

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2019/02/13 05:29

ほかの人のために書いておきます。 仕方のない選択ではありません。 無知故の間違った結論です。
guest

0

とりあえずZuishinさんのタスクをキューに入れたバージョンで、相変わらずロックなどの同期は必要なもののMonitorを使用しない形には出来るので、書いておきます。コメントで(1)と(2)を出していましたが、その間といったところのものを作ってみました。
エラー処理とメモリが心配な作りになっていますが、今の所他に問題は見つかっていません。一応ちゃんと書いておくと、控えめに言っても多分あまりRectiveな実装ではなく、また元の実装と比較して効率がいいわけでもありませんが、aync/awaitで非同期のReadメソッドが簡単に作成できます。

数日待って問題の指摘がなければ解決とさせて頂きます。

C#

1using System; 2using System.Collections.Generic; 3using System.Reactive; 4using System.Reactive.Linq; 5using System.Reactive.Threading.Tasks; 6using System.Threading.Tasks; 7 8namespace EventAsyncAwait 9{ 10 interface Reader 11 { 12 int Read(); 13 } 14 15 class MyEventArgs 16 { 17 public int val { get; set; } 18 } 19 20 class MyReader : Reader 21 { 22 public int Read() 23 { 24 TaskContext context; 25 lock (_queue) 26 { 27 context = _queue.Dequeue(); 28 } 29 context.Task.Wait(); 30 context.Observer.Dispose(); 31 return context.Args.EventArgs.val; 32 } 33 34 class TaskContext 35 { 36 public EventPattern<MyEventArgs> Args { set; get; } 37 public Task Task { set; get; } 38 public IDisposable Observer { set; get; } 39 } 40 41 Queue<TaskContext> _queue = new Queue<TaskContext>(); 42 43 private void CreateTask(IObservable<EventPattern<MyEventArgs>> subject) 44 { 45 var observable = subject.Take(1); 46 var elm = new TaskContext(); 47 elm.Observer = observable.Subscribe((args) => { 48 elm.Args = args; 49 CreateTask(subject); 50 }); 51 elm.Task = observable.ToTask(); 52 lock(_queue) 53 { 54 _queue.Enqueue(elm); 55 } 56 } 57 58 public MyReader(EventHolder parent) 59 { 60 CreateTask( 61 Observable.FromEventPattern<MyEventArgs>( 62 h => parent.Event += h, 63 h => parent.Event -= h)); 64 } 65 } 66 67 class EventHolder 68 { 69 public event EventHandler<MyEventArgs> Event; 70 71 public void Fire(int v) 72 { 73 Event(this, new MyEventArgs() { val = v }); 74 } 75 } 76 77 class Program 78 { 79 static EventHolder holder = new EventHolder(); 80 81 static void Main(string[] args) 82 { 83 MyReader reader = new MyReader(holder); 84 Task.Run(async () => 85 { 86 await Task.Delay(3000); 87 holder.Fire(5); 88 }); 89 Console.WriteLine(reader.Read()); 90 } 91 } 92} 93

投稿2019/02/11 10:03

wwbQzhMkhhgEmhU

総合スコア343

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問