async
メソッドがあります。
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
このメソッドは同期メソッドから呼び出す必要があります。
これを同期的に機能させるためにGenerateCodeAsync
メソッドを複製することなくこれを行うにはどうすればよいですか
アップデート
まだ合理的な解決策は見つかりませんでした。
しかし、私はHttpClient
がすでにこれを実装しているのを見ます pattern
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
タスクのResult
プロパティにアクセスできます。これにより、結果が利用可能になるまでスレッドはブロックされます。
string code = GenerateCodeAsync().Result;
注:場合によっては、これによってデッドロックが発生する可能性があります。Result
を呼び出すとメインスレッドがブロックされるため、残りの非同期コードが実行されなくなります。これが起こらないようにするには、以下のオプションがあります。
.ConfigureAwait(false)
を追加します または スレッドプールthreadで非同期メソッドを明示的に実行し、それが終了するのを待ちます。
string code = Task.Run(GenerateCodeAsync).Result;
待機者(GetAwaiter()
)を取得し、非同期タスク(GetResult()
)の完了を待つ必要があります。
string code = GenerateCodeAsync().GetAwaiter().GetResult();
あなたはデリゲート、ラムダ式を使ってこれを成し遂げることができるはずです
private void button2_Click(object sender, EventArgs e)
{
label1.Text = "waiting....";
Task<string> sCode = Task.Run(async () =>
{
string msg =await GenerateCodeAsync();
return msg;
});
label1.Text += sCode.Result;
}
private Task<string> GenerateCodeAsync()
{
return Task.Run<string>(() => GenerateCode());
}
private string GenerateCode()
{
Thread.Sleep(2000);
return "I m back" ;
}
このメソッドは同期メソッドから呼び出す必要があります。
他の答えが示唆するように、それはGenerateCodeAsync().Result
またはGenerateCodeAsync().Wait()
で可能です。 GenerateCodeAsync
が完了するまで、これは現在のスレッドをブロックします。
しかし、あなたの質問は asp.net でタグ付けされていて、あなたもコメントを残しました:
Asp.netが非常に多くのコード行を書くよりはるかに簡単にこれを処理すると考えて、私はもっと簡単な解決策を望んでいました。
私の言いたいことは、ASP.NETの非同期メソッドではブロックしてはいけませんです。これはWebアプリケーションのスケーラビリティを低下させ、デッドロックを引き起こす可能性があります(await
内のGenerateCodeAsync
継続がAspNetSynchronizationContext
に投稿された場合)。 Task.Run(...).Result
を使用して何かをプールスレッドにオフロードしてからブロックすると、特定のHTTPリクエストを処理するスレッドがさらに+1されるため、スケーラビリティがさらに低下します。
ASP.NETは、非同期コントローラ(ASP.NET MVCおよびWeb API)、または従来のASP.NETのAsyncManager
およびPageAsyncTask
を介して、非同期メソッドを組み込みでサポートしています。あなたはそれを使うべきです。詳しくは この答え をチェックしてください。
Microsoft Identityには、非同期メソッドを同期的に呼び出す拡張メソッドがあります。例えば、GenerateUserIdentityAsync()メソッドと等しいCreateIdentity()があります。
UserManagerExtensions.CreateIdentity()を見ると、これは次のようになります。
public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
string authenticationType)
where TKey : IEquatable<TKey>
where TUser : class, IUser<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
}
AsyncHelper.RunSyncが何をするのか見てみましょう。
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
var cultureUi = CultureInfo.CurrentUICulture;
var culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew(() =>
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap().GetAwaiter().GetResult();
}
だから、これは非同期メソッドのラッパーです。結果からデータを読み込まないでください - それは潜在的にASPのあなたのコードをブロックするでしょう。
別の方法があります - 私にとっては疑わしいのですが、あなたもそれを考慮することができます
Result r = null;
YourAsyncMethod()
.ContinueWith(t =>
{
r = t.Result;
})
.Wait();
デッドロックを防ぐために、@ Heinziが言及している非同期メソッドを同期的に呼び出す必要があるときは、常にTask.Run()
を使用します。
ただし、非同期メソッドがパラメータを使用する場合は、メソッドを変更する必要があります。例えばTask.Run(GenerateCodeAsync("test")).Result
はエラーを出します:
引数1: '
System.Threading.Tasks.Task<string>
'から 'System.Action'に変換できません
これは代わりに次のように呼び出すことができます。
string code = Task.Run(() => GenerateCodeAsync("test")).Result;
他の方法は、タスクが終了するまで待つことです。
var t = GenerateCodeService.GenerateCodeAsync();
Task.WhenAll(t);
string code = t.Result;