🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

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

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

4回答

854閲覧

async を伴うメソッド定義の記述方法と戻り値について

twyujiro15

総合スコア217

C#

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

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2021/03/18 03:54

編集2021/03/18 03:56

C#6.0
.NET Framework 4.6 で開発する上で、
非同期処理について調査をしています。

async/await を使った書き方について、
例えば次のような書き方ができると思います。

C#

1private Task Hoge() 2{ 3 return Task.Run(() => DoWork()); 4} 5 6private async Task HogeAsync() 7{ 8 await Task.Run(() => DoWork()); 9} 10 11private void DoWork() 12{ 13 // 何か処理 14}

Hoge() はタスクを明示的に返すだけのものですが、
HogeAsync() は非同期的に処理をおこなおうとしています。

ところで、この HogeAsync() メソッドの定義の記述方法なのですが、
Task クラスを戻り値として指定していますが、
明示的に return していないため、
通常のメソッドとして見ると違和感を覚えます。
これは await キーワードの裏側に return があるのでしょうか?
その場合、

C#

1private async Task HogeAsync() 2{ 3 await Task.Run(() => DoWork1()); 4 await Task.Run(() => DoWork2()); 5}

このように複数の await を使用するとどうなるのでしょうか。

それとも、async キーワードを伴うメソッドは、
このメソッド全体をタスクとして認識し、
メソッド全体の処理そのものを Task クラスの戻り値とするのでしょうか。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/03/19 00:53

質問者さん、無言ですが、回答がついていますのでそれらに対するフィードバックを書いてください。役に立った/立たなかったぐらいはすぐ回答できるのでは? 役に立たなかったなら何が期待する答えと違うかを書けば、期待する回答が出てくるかも。とにかく無言は NG です。
twyujiro15

2021/03/23 14:22

申し訳ありません。タイミング悪く、質問を投げてからしばらくパソコンの環境から離れてしまっていました。意図的ではなかったとはいえ、しばらく無言になってしまい、ご回答いただいた皆様に不快な思いをさせてしまったことをお詫びします。
退会済みユーザー

退会済みユーザー

2021/03/23 21:49

質問のコメント (Task を return しないのはなぜ?) についてはレスを返しましたので見てください。 それでこのスレッドの疑問は解決したと思いますが、であればこのスレッドはクローズしてください。前のスレッドもクローズ願います。
guest

回答4

0

ベストアンサー

これは await キーワードの裏側に return があるのでしょうか?

実際にコンパイラは

csharp

1using System; 2using System.Threading.Tasks; 3 4 5class Test { 6 7 public async Task HogeAsync() 8 { 9 await Task.Run(() => DoWork1()); 10 await Task.Run(() => DoWork2()); 11 } 12 13 private void DoWork1() {} 14 private void DoWork2() {} 15 16}

このコードを

csharp

1using System; 2using System.Diagnostics; 3using System.Reflection; 4using System.Runtime.CompilerServices; 5using System.Security; 6using System.Security.Permissions; 7using System.Threading.Tasks; 8 9[assembly: CompilationRelaxations(8)] 10[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] 11[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] 12[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] 13[assembly: AssemblyVersion("0.0.0.0")] 14[module: UnverifiableCode] 15internal class Test 16{ 17 [CompilerGenerated] 18 private sealed class <HogeAsync>d__0 : IAsyncStateMachine 19 { 20 public int <>1__state; 21 22 public AsyncTaskMethodBuilder <>t__builder; 23 24 public Test <>4__this; 25 26 private TaskAwaiter <>u__1; 27 28 private void MoveNext() 29 { 30 int num = <>1__state; 31 try 32 { 33 TaskAwaiter awaiter; 34 TaskAwaiter awaiter2; 35 if (num != 0) 36 { 37 if (num == 1) 38 { 39 awaiter = <>u__1; 40 <>u__1 = default(TaskAwaiter); 41 num = (<>1__state = -1); 42 goto IL_00ef; 43 } 44 awaiter2 = Task.Run(new Action(<>4__this.<HogeAsync>b__0_0)).GetAwaiter(); 45 if (!awaiter2.IsCompleted) 46 { 47 num = (<>1__state = 0); 48 <>u__1 = awaiter2; 49 <HogeAsync>d__0 stateMachine = this; 50 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); 51 return; 52 } 53 } 54 else 55 { 56 awaiter2 = <>u__1; 57 <>u__1 = default(TaskAwaiter); 58 num = (<>1__state = -1); 59 } 60 awaiter2.GetResult(); 61 awaiter = Task.Run(new Action(<>4__this.<HogeAsync>b__0_1)).GetAwaiter(); 62 if (!awaiter.IsCompleted) 63 { 64 num = (<>1__state = 1); 65 <>u__1 = awaiter; 66 <HogeAsync>d__0 stateMachine = this; 67 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); 68 return; 69 } 70 goto IL_00ef; 71 IL_00ef: 72 awaiter.GetResult(); 73 } 74 catch (Exception exception) 75 { 76 <>1__state = -2; 77 <>t__builder.SetException(exception); 78 return; 79 } 80 <>1__state = -2; 81 <>t__builder.SetResult(); 82 } 83 84 void IAsyncStateMachine.MoveNext() 85 { 86 //ILSpy generated this explicit interface implementation from .override directive in MoveNext 87 this.MoveNext(); 88 } 89 90 [DebuggerHidden] 91 private void SetStateMachine(IAsyncStateMachine stateMachine) 92 { 93 } 94 95 void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) 96 { 97 //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine 98 this.SetStateMachine(stateMachine); 99 } 100 } 101 102 [AsyncStateMachine(typeof(<HogeAsync>d__0))] 103 [DebuggerStepThrough] 104 public Task HogeAsync() 105 { 106 <HogeAsync>d__0 stateMachine = new <HogeAsync>d__0(); 107 stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create(); 108 stateMachine.<>4__this = this; 109 stateMachine.<>1__state = -1; 110 stateMachine.<>t__builder.Start(ref stateMachine); 111 return stateMachine.<>t__builder.Task; 112 } 113 114 private void DoWork1() 115 { 116 } 117 118 private void DoWork2() 119 { 120 } 121 122 [CompilerGenerated] 123 private void <HogeAsync>b__0_0() 124 { 125 DoWork1(); 126 } 127 128 [CompilerGenerated] 129 private void <HogeAsync>b__0_1() 130 { 131 DoWork2(); 132 } 133}

こう変換してる。

HogeAsync()を見ての通り、ちゃんとreturnしてます。

投稿2021/03/18 07:11

gentaro

総合スコア8947

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

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

twyujiro15

2021/03/23 14:47

ご回答ありがとうございます。 実際のコードまで掲載いただいたので大変参考になりました。 つまり、await の裏側に return があり、 さらに、メソッド全体をタスクとして認識し、 HogeAsync() メソッドに記述した処理全体を Task クラスとしての戻り値とする、 という理解は概ね間違いではない、ということで良いのでしょうか。
guest

0

Task クラスを戻り値として指定していますが、

明示的に return していないため、
通常のメソッドとして見ると違和感を覚えます。

戻り値の型が Task の非同期メソッドは呼び出し元に値を返しません。
詳しくは 非同期の戻り値の型 (C#) を参照してください。

これは await キーワードの裏側に return があるのでしょうか?

IL をデコンパイルした C# のコードを確認したら return ステートメントに相応する記述があるかもしれませんが、私には分かりません。
しかし、少なくとも Task の非同期メソッドに return ステートメントを記述する必要はありません。
もちろん、明示的に return ステートメントを記述することはできます。

このように複数の await を使用するとどうなるのでしょうか。

DoWork1 が非同期で呼び出され、DoWork1 が完了するまで HogeAsync は保留となり、DoWork1 が完了すると HogeAsync は再開します。DoWork2 も同様です。

また、どちらのメソッドも値は返されません。

async キーワードを伴うメソッドは、

このメソッド全体をタスクとして認識し、
メソッド全体の処理そのものを Task クラスの戻り値とするのでしょうか。

概ね正しいと思います。
Task クラスに限らず他の型に関しても非同期メソッドの戻り値に指定することが出来ます。

投稿2021/03/18 04:54

編集2021/03/23 15:43
BluOxy

総合スコア2663

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

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

BluOxy

2021/03/18 04:57

ゆっくり回答を書いていたので、radian さんと内容が被ってしまいました。
twyujiro15

2021/03/23 14:32

ご回答ありがとうございます。 処理の流れ自体は理解していますが、 メソッドの書き方として、 HogeAsync() メソッドは Task クラスのオブジェクトを返しているはずなのに、 何も返していないような記述になることについて疑問を抱いていました。
BluOxy

2021/03/23 15:44 編集

気持ちは分からなくもないです。しかし、言語仕様でそう決められているようです。 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/expressions#function-members > Fが async で、F の本体が Nothing (式の分類) として分類された式であるか、または return ステートメントに式が含まれていないステートメントブロックである場合、推論される戻り値の型は System.Threading.Tasks.Task です。 ※若干ガバ翻訳は直しています
guest

0

違和感を覚えると言われても、返り値を持たない非同期メソッドの定義がそうだからとしか言いようがないです。
非同期の戻り値の型 (C#)

async, awaitは只の糖衣構文なので、実際に内部でどうコンパイルされ、どういう処理をしているかが気になるのであれば、ILSpy等で逆コンパイルして覗いてみればよいのではないでしょうか。

投稿2021/03/18 04:34

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

twyujiro15

2021/03/23 14:26

ご回答ありがとうございます。 確かに ILSpy などで確認すべきでした。アドバイスありがとうございます。
guest

0

質問者さんの別のスレッドで紹介した記事の「async void を避ける」というセクションの説明が参考になりませんか?

async void を避ける
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#async-void-%E3%82%92%E9%81%BF%E3%81%91%E3%82%8B

以下一部抜粋:

"async Task メソッドまたは async Task<T> メソッドから例外がスローされると、その例外は Task オブジェクトでキャプチャされ、そこで処理されます"

投稿2021/03/18 05:42

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

twyujiro15

2021/03/23 14:40

ご回答ありがとうございます。 少しややこしいですが、 私の言うところの「戻り値」というのは、 非同期処理の結果というわけではなく、 HogeAsync() メソッド自身の戻り値のことです。 例えば int Calc() { } と書かれたメソッドが int 型を返すように、 Task Hoge() { } は Task クラスオブジェクトを return で返していますが、 async Task HogeAsync() { } は Task クラスオブジェクトを return で返していないような記述になっていることに疑問を抱いていました。
退会済みユーザー

退会済みユーザー

2021/03/23 21:37

> async Task HogeAsync() { } は Task クラスオブジェクトを return で返していないような記述になっていることに疑問を抱いていました。 async キーワードがあるので自動的に Task を返してくれるということのようです。 Task を返すメソッドに async キーワードをつけない場合は return Task.CompletedTask; のようにします。どういうケースがあるかというと、例えば Interface に定義されているメソッドが Task を返すが実際には非同期にする必要はないという場合があります。具体例は以下の記事のコードを見てください。 http://surferonwww.info/BlogEngine/post/2020/09/06/rolestore-custom-storage-providers.aspx
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問