C#で同期メソッドから非同期メソッドを呼び出す方法は?


863

私が持っているpublic async void Foo()私は、同期メソッドから呼び出したいというメソッドを。これまでのところ、MSDNドキュメントから見たのは、非同期メソッドを介して非同期メソッドを呼び出すことだけですが、プログラム全体が非同期メソッドで構築されていません。

これは可能ですか?

以下は、非同期メソッドからこれらのメソッドを呼び出す1つの例です。http//msdn.microsoft.com/en-us/library/hh300224(v = vs.110).aspx

今、私はこれらの非同期メソッドを同期メソッドから呼び出すことを検討しています。


2
私もこれに遭遇しました。RoleProviderをオーバーライドすると、GetRolesForUserメソッドのメソッドシグネチャを変更できないため、メソッドを非同期にできず、待機を使用してAPIを非同期で呼び出すことができません。私の一時的な解決策は、汎用のHttpClientクラスに同期メソッドを追加することでしたが、これが可能かどうか(およびその影響)を知りたいです。
ティモシーリーラッセル

1
async void Foo()メソッドはを返さないためTask、呼び出し元はいつ完了したかを知ることができないため、Task代わりに返す必要があります。

1
UIスレッドでこれを行う方法に関する関連するQ / Aのリンク。
noseratio

回答:


711

非同期プログラミングは、コードベースを通じて「成長」します。ゾンビウイルスと比較されています。最善の解決策は、それを成長させることですが、それができない場合もあります。

部分的に非同期のコードベースを処理するために、Nito.AsyncExライブラリにいくつかの型を記述しました。ただし、すべての状況で機能する解決策はありません。

ソリューションA

コンテキストに同期する必要のない単純な非同期メソッドがある場合は、次を使用できますTask.WaitAndUnwrapException

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

あなたはないではない使用したいTask.Waitか、Task.Result彼らは例外をラップしているためAggregateException

このソリューションは、MyAsyncMethodがそのコンテキストに同期しない場合にのみ適切です。つまり、すべての入力awaitMyAsyncMethodで終わる必要がありConfigureAwait(false)ます。つまり、UI要素を更新したり、ASP.NET要求コンテキストにアクセスしたりすることはできません。

ソリューションB

MyAsyncMethodコンテキストに同期する必要がある場合はAsyncContext.RunTask、ネストされたコンテキストを提供するために使用できる場合があります。

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

* 2014年4月14日更新:ライブラリの最新バージョンでは、APIは次のとおりです。

var result = AsyncContext.Run(MyAsyncMethod);

(例外が伝播するTask.Resultため、この例でRunTaskは使用しても問題ありませTaskん)。

AsyncContext.RunTask代わりに必要になるTask.WaitAndUnwrapException理由は、WinForms / WPF / SL / ASP.NETで発生するかなり微妙なデッドロックの可能性があるためです。

  1. 同期メソッドは非同期メソッドを呼び出し、を取得しTaskます。
  2. 同期メソッドは、でブロッキング待機を行いTaskます。
  3. asyncこの方法は、使用していますawaitなしConfigureAwait
  4. Taskときにのみ完了するので、このような状況で完了することができないasync方法で、完成です。asyncこの方法は、完全ではないにその継続をスケジュールしようとしている可能性があるためSynchronizationContext、とのWinForms / WPF / SL / ASP.NETは、同期方法は、すでにそのコンテキストで実行されているので、継続が実行することはできません。

これが、ConfigureAwait(false)すべてのasyncメソッド内で可能な限り使用することが推奨される理由の1つです。

ソリューションC

AsyncContext.RunTaskすべてのシナリオで機能するわけではありません。たとえば、asyncメソッドがUIイベントの完了を必要とする何かを待機している場合、ネストされたコンテキストでもデッドロックが発生します。その場合、asyncスレッドプールでメソッドを開始できます。

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

ただし、このソリューションではMyAsyncMethod、スレッドプールコンテキストで機能するが必要です。したがって、UI要素を更新したり、ASP.NET要求コンテキストにアクセスしたりすることはできません。その場合は、ConfigureAwait(false)そのawaitステートメントに追加して、ソリューションAを使用することもできます。

更新、2019-05-01:現在の「最悪の慣行」は、こちらのMSDN記事にあります


9
ソリューションAは私が望んでいるように見えますが、task.WaitAndUnwrapException()は.Net 4.5 RCに含まれていません。task.Wait()しかありません。新しいバージョンでこれを行う方法はありますか?それとも、あなたが書いたカスタム拡張メソッドですか?
deadlydog

3
WaitAndUnwrapException私のAsyncExライブラリの独自のメソッドです。公式の.NETライブラリは、同期コードと非同期コードを混在させるための多くのヘルプを提供していません(そして、一般的に、それを行うべきではありません!)。.NET 4.5 RTWと新しい非XPラップトップを待ってから、AsyncExを4.5で実行するように更新します(XPでさらに数週間作業ができないため、現在4.5で開発できません)。
Stephen Cleary

12
AsyncContext今持っているRunあなたが使用する必要がありますので、ラムダ式を取る方法var result = AsyncContext.Run(() => MyAsyncMethod());
ステファン・クリアリー

1
ライブラリをNugetから外しましたが、実際にはRunTaskメソッドがないようです。私が見つけた最も近いものはでしたがRun、それはResultプロパティを持っていません。
Asad Saeeduddin 2014

3
@アサド:はい、2年以上後にAPIが変更されました。簡単に言うとvar result = AsyncContext.Run(MyAsyncMethod);
スティーブンクリアリー2014

313

最終的に私の問題を解決したソリューションを追加すると、誰かの時間を節約できると思います。

まず、Stephen Clearyの記事をいくつか読んでください。

「非同期コードでブロックしない」の「2つのベストプラクティス」によると、最初の方法は機能せず、2番目の方法は適用できませんでした(基本的に、を使用できる場合は使用awaitします)。

だからここに私の回避策があります:呼び出しをの中にラップし、Task.Run<>(async () => await FunctionAsync());うまくいけばデッドロックはもうありません。

これが私のコードです:

public class LogReader
{
    ILogger _logger;

    public LogReader(ILogger logger)
    {
        _logger = logger;
    }

    public LogEntity GetLog()
    {
        Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
        return task.Result;
    }

    public async Task<LogEntity> GetLogAsync()
    {
        var result = await _logger.GetAsync();
        // more code here...
        return result as LogEntity;
    }
}

5
2年後、このソリューションがどのように機能しているのか知りたいです。連絡あった?初心者に失われているこのアプローチに微妙な点はありますか?
Dan Esparza 2017

26
これはデッドロックにはなりませんが、それは単に、元のスレッドの同期コンテキストの外で、新しいスレッドで強制的に実行されるためです。ただし、これが非常に不適切である特定の環境があります。特にWebアプリケーションです。これにより、Webサーバーで使用可能なスレッドが半分になります(1つのスレッドが要求用で、もう1つがこれ用)。これを実行すればするほど、悪化します。Webサーバー全体がデッドロックになる可能性があります。
Chris Pratt

30
@ChrisPratt- Task.Run()非同期コードのベストプラクティスではないため、あなたは正しいかもしれません。しかし、再び、元の質問に対する答えは何ですか?非同期メソッドを同期的に呼び出すことはありませんか?私たちは願っていますが、現実の世界では、時にはそうしなければなりません。
Tohid

1
@Tohidスティーブンクリアリーのライブラリを試すことができます。私は人々がこれを想定し、Parallel.ForEach虐待が「現実の世界」に影響を及ぼさず、最終的にサーバーを停止させるのを見てきました。このコードはコンソールアプリでは問題ありませんが、@ ChrisPrattが言うように、Webアプリでは使用しないでください。「今」機能するかもしれませんが、スケーラブルではありません。
makhdumi 2017年

1
私は.... SOこの1つだけをupvoteするのに十分なポイントを得るために質問に答える新しいaccountsinの作成を開始するために興味をそそられています
Giannis Paraskevopoulos

206

Microsoftは、AsyncをSyncとして実行するためのAsyncHelper(内部)クラスを作成しました。ソースは次のようになります。

internal static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new 
      TaskFactory(CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return AsyncHelper._myTaskFactory
          .StartNew<Task<TResult>>(func)
          .Unwrap<TResult>()
          .GetAwaiter()
          .GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

Microsoft.AspNet.Identity基本クラスにはAsyncメソッドのみがあり、それらをSyncとして呼び出すために、次のような拡張メソッドを持つクラスがあります(使用例)。

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}

public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

コードのライセンス条件が気になる方のために、非常によく似たコードへのリンクを示します(スレッドにカルチャのサポートを追加するだけです)。これは、マイクロソフトによってMITライセンスされていることを示すコメントがあります。 https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


2
私の非同期メソッドは他の非同期メソッドを待ちます。私は自分のawait通話をで飾りませんConfigureAwait(false)。を使用AsyncHelper.RunSyncApplication_Start()てGlobal.asaxの関数から非同期関数を呼び出すと、動作するようです。これは、AsyncHelper.RunSync私がこの投稿の他の場所で読んだ「呼び出し元のコンテキストへのマーシャルバック」デッドロックの問題が発生しにくいことを意味しますか?
Bob.at.Indigo.Health 2015年

1
@ Bob.at.SBSは、コードの内容によって異なります。このコードを使用する場合ほど簡単ではありません。これは非同期コマンドを同期的に実行するための非常に最小限かつ安全な方法であり、不適切に使用してデッドロックを引き起こす可能性があります。
エリックフィリップス2015年

1
ありがとう。2つのフォローアップ質問:1)非同期メソッドがデッドロックの原因となる回避したい例を挙げていただけますか?2)このコンテキストのデッドロックはタイミングに依存することが多いですか?それが実際に機能する場合、コードにタイミング依存のデッドロックが潜んでいる可能性がありますか?
Bob.at.Indigo.Health 2015年

@ Bob.at.SBS右上の[ 質問する ]ボタンを使用して質問することをお勧めします。参照として、この質問へのリンクまたは質問への回答を含めることができます。
エリックフィリップス

1
@ Bob.at ... Erikが提供するコードはAspの下で完璧に動作します。ネットmvc5とEF6、ただし、他のソリューション(ConfigureAwait(false).GetAwaiter()。GetResult()または.result)を試してみたところ、私のWebアプリが完全にハングしました
LeonardoX

151

async MainはC#7.2の一部になり、プロジェクトの詳細ビルド設定で有効にすることができます。

C#<7.2の場合、正しい方法は次のとおりです。

static void Main(string[] args)
{
   MainAsync().GetAwaiter().GetResult();
}


static async Task MainAsync()
{
   /*await stuff here*/
}

これは、Microsoftの多くのドキュメントで使用されていることがわかります。たとえば、https//docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use-トピック-サブスクリプション


11
なぜ誰かがこれに反対票を投じたのか、私にはわかりません。これは私にとってはうまくいきました。この修正がなければ、ASYCH EVERYWHEREを伝播する必要があったでしょう。
囚人ZERO

11
なぜこれよりも優れているのMainAsync().Wait()ですか?
ときめき

8
同意する。このすべてではなく、MainAsync()。Wait()が必要です。
2015

8
@crushデッドロックを回避する方法を説明しました。状況によっては、UIまたはasp.netスレッドから.Wait()を呼び出すと、デッドロックが発生します。非同期デッドロック
David

6
@ClintB:ASP.NET Coreでは絶対にこれを行わないでください。Webアプリケーションは特にスレッドスターブに脆弱であり、これを実行するたびに、プールからスレッドをプルします。そうでなければ、リクエストの処理に使用されます。デスクトップ/モバイルアプリケーションは、従来はシングルユーザーであるため、問題が少なくなります。
Chris Pratt、

52
public async Task<string> StartMyTask()
{
    await Foo()
    // code to execute once foo is done
}

static void Main()
{
     var myTask = StartMyTask(); // call your method which will return control once it hits await
     // now you can continue executing code here
     string result = myTask.Result; // wait for the task to complete to continue
     // use result

}

「await」キーワードを「この長時間実行タスクを開始してから、制御を呼び出しメソッドに戻す」と読みます。実行時間の長いタスクが完了すると、その後にコードが実行されます。待機後のコードは、以前はCallBackメソッドであったものに似ています。論理的なフローの大きな違いは中断されないため、書き込みと読み取りがはるかに簡単になります。


15
Wait例外をラップし、デッドロックの可能性があります。
スティーブンクリアリー

を使用せずに非同期メソッドを呼び出すと、await同期的に実行されると思いました。少なくとも私には有効です(を呼び出さなくてもmyTask.Wait)。実際、myTask.RunSynchronously()既に実行されているため、呼び出しをしようとしたときに例外が発生しました。
2018年

2
私はこの答えが好きです。小さくてエレガントな編集のための良いコメント。貢献してくれてありがとう!私はまだ並行性を学んでいるので、すべてが役に立ちます:)
kayleeFrye_onDeck

2
この回答は今日でも機能しますか?私はMVC Razorプロジェクトで試してみましたが、アプリはアクセス中にハングし.Resultます。
コーディング終了

8
@TrueBlueAussieこれが同期コンテキストのデッドロックです。非同期コードは同期コンテキストにマーシャリングしますが、Resultその時点では呼び出しによってブロックされているため、そこに到達することはありません。そして、Resultそれが終わるのを待っている誰かを待っているのでResult、終わらない、基本的に:D
Luaan

40

100%確実ではありませんが、このブログで説明されている手法は多くの状況で機能するはずです。

したがって、task.GetAwaiter().GetResult()この伝播ロジックを直接呼び出す場合に使用できます。


6
上記のStephen Clearyの回答のソリューションAは、この方法を使用しています。WaitAndUnwrapExceptionソースを参照してください。
orad

呼び出す関数がvoidまたはタスクである場合、GetResult()を使用する必要がありますか?
つまり

はい、それ以外の場合はタスクが完了するまでブロックされません。または、GetAwaiter()。GetResult()を呼び出す代わりに、.Wait()を呼び出すことができます
NStuke

1
それは「多くの状況」の部分です。これは、全体的なスレッドモデルと、デッドロックのリスクがあるかどうかを判断するために他のスレッドが何をしているかによって異なります。
NStuke 2018年

GetAwaiter()。GetResult()は依然としてデッドロックを引き起こす可能性があります。例外をより適切なものにラップするだけです。
nawfal

25

ただし、アドホックメッセージポンプ(SynchronizationContext)のすべての状況で(ほぼ:コメントを参照)で機能する優れたソリューションがあります。

呼び出しスレッドは期待どおりにブロックされますが、非同期スレッドから呼び出されたすべての継続は、呼び出しスレッドで実行されているアドホックSynchronizationContext(メッセージポンプ)にマーシャリングされるため、デッドロックしないことが保証されます。

アドホックメッセージポンプヘルパーのコード:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Threading
{
    /// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
    public static class AsyncPump
    {
        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Action asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(true);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function
                syncCtx.OperationStarted();
                asyncMethod();
                syncCtx.OperationCompleted();

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Func<Task> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static T Run<T>(Func<Task<T>> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                return t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
        private sealed class SingleThreadSynchronizationContext : SynchronizationContext
        {
            /// <summary>The queue of work items.</summary>
            private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
                new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
            /// <summary>The processing thread.</summary>
            private readonly Thread m_thread = Thread.CurrentThread;
            /// <summary>The number of outstanding operations.</summary>
            private int m_operationCount = 0;
            /// <summary>Whether to track operations m_operationCount.</summary>
            private readonly bool m_trackOperations;

            /// <summary>Initializes the context.</summary>
            /// <param name="trackOperations">Whether to track operation count.</param>
            internal SingleThreadSynchronizationContext(bool trackOperations)
            {
                m_trackOperations = trackOperations;
            }

            /// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
            /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
            /// <param name="state">The object passed to the delegate.</param>
            public override void Post(SendOrPostCallback d, object state)
            {
                if (d == null) throw new ArgumentNullException("d");
                m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
            }

            /// <summary>Not supported.</summary>
            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("Synchronously sending is not supported.");
            }

            /// <summary>Runs an loop to process all queued work items.</summary>
            public void RunOnCurrentThread()
            {
                foreach (var workItem in m_queue.GetConsumingEnumerable())
                    workItem.Key(workItem.Value);
            }

            /// <summary>Notifies the context that no more work will arrive.</summary>
            public void Complete() { m_queue.CompleteAdding(); }

            /// <summary>Invoked when an async operation is started.</summary>
            public override void OperationStarted()
            {
                if (m_trackOperations)
                    Interlocked.Increment(ref m_operationCount);
            }

            /// <summary>Invoked when an async operation is completed.</summary>
            public override void OperationCompleted()
            {
                if (m_trackOperations &&
                    Interlocked.Decrement(ref m_operationCount) == 0)
                    Complete();
            }
        }
    }
}

使用法:

AsyncPump.Run(() => FooAsync(...));

非同期ポンプの詳細については、こちらをご覧ください


例外のコンテキストとAsyncPumpの stackoverflow.com/questions/23161693/...
PreguntonCojoneroCabrón

これは、Asp.netシナリオでは機能しません。ランダムにHttpContext.Currentを失う可能性があるためです。
Josh Mouch、

12

この質問にもう注意を払っている人には...

調べてみると、Microsoft.VisualStudio.Services.WebApiというクラスがありますTaskExtensions。そのクラス内に静的拡張メソッドが表示されますTask.SyncResult()、タスクが戻るまでスレッドを完全にブロックするようなます。

内部的にtask.GetAwaiter().GetResult()は非常に単純な呼び出しですがasync、return TaskTask<T>またはTask<HttpResponseMessage>...構文糖、ベイビー...パパは甘い歯を持っているメソッドで作業するためにオーバーロードされています。

ように見え...GetAwaiter().GetResult()、ブロッキングコンテキストで非同期コードを実行するMS-公式の方法です。私のユースケースでは非常にうまく機能しているようです。


3
あなたは「まるでただのブロックのように」私を迎えました。
Dawood ibnカリーム

9
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);

OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

またはこれを使用してください:

var result=result.GetAwaiter().GetResult().AccessToken

6

同期コードから非同期メソッドを呼び出すことができます。つまり、非同期コードをawaitオンにする必要があるまで呼び出すことができますasync。その場合、それらもマークする必要があります。

多くの人がここで提案しているように、同期メソッドの結果のタスクでWait()またはResultを呼び出すことはできますが、そのメソッドではブロッキング呼び出しが発生し、非同期の目的に反します。

メソッドを実際に作成できずasync、同期メソッドをロックしたくない場合は、タスクのContinueWithメソッドにパラメーターとして渡して、コールバックメソッドを使用する必要があります。


5
次に、それはメソッドを同期的に呼び出さないでしょうか?
ジェフメルカド

2
私が理解しているように、問題は非同期メソッドから非同期メソッドを呼び出すことができるかということでした。これは、ブロッキングメソッドで非同期メソッドを呼び出す必要があることを意味しません。
base2

申し訳ありませんが、あなたの「彼らにもマークをasync付ける必要があります」は、あなたが本当に言っていることから私の注意をそらしました。
ジェフメルカド

私が非同期性について本当に気にしていない場合、この方法で呼び出しても問題ありませんか(そして、Stephen Clearyがしつこいラップしている例外でデッドロックが発生する可能性はありますか?)いくつかのテストメソッドがあります(同期的に実行する必要があります)。非同期メソッドをテストします。結果を待ってから続行する必要があるので、非同期メソッドの結果をテストできます。
2018年

6

私はとても遅いことを知っています。しかし、私のような誰かがこれをきちんと簡単な方法で、別のライブラリに依存せずに解決したいと思った場合に備えて。

私はライアンから次のコードを見つけました

public static class AsyncHelpers
{
    private static readonly TaskFactory taskFactory = new
        TaskFactory(CancellationToken.None,
            TaskCreationOptions.None,
            TaskContinuationOptions.None,
            TaskScheduler.Default);

    /// <summary>
    /// Executes an async Task method which has a void return value synchronously
    /// USAGE: AsyncUtil.RunSync(() => AsyncMethod());
    /// </summary>
    /// <param name="task">Task method to execute</param>
    public static void RunSync(Func<Task> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    /// <summary>
    /// Executes an async Task<T> method which has a T return type synchronously
    /// USAGE: T result = AsyncUtil.RunSync(() => AsyncMethod<T>());
    /// </summary>
    /// <typeparam name="TResult">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static TResult RunSync<TResult>(Func<Task<TResult>> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

次に、このようにそれを呼び出すことができます

var t = AsyncUtil.RunSync<T>(() => AsyncMethod<T>());

6
これは上記の回答とまったく同じです。何か不足しています
inlokesh

2

さまざまな方法を何時間か試した後、多かれ少なかれ成功したので、これで終わりました。結果の取得中にデッドロックで終了することはなく、ラップされた例外ではなく、元の例外を取得してスローすることもできます。

private ReturnType RunSync()
{
  var task = Task.Run(async () => await myMethodAsync(agency));
  if (task.IsFaulted && task.Exception != null)
  {
    throw task.Exception;
  }

  return task.Result;
}

1
return task.GetAwaiter()。GetResult();で動作します。
G

はい、しかし元の例外はどうですか?
イジーHerník

.Resultは基本的に.GetAwaiter()。GetResult()と同じだと思います
G

-2

新しいスレッドから呼び出すことができます(スレッドプールからではありません!):

public static class SomeHelperClass
{ 
       public static T Result<T>(Func<T> func)
        {
            return Task.Factory.StartNew<T>(
                  () => func()
                , TaskCreationOptions.LongRunning
                ).Result;
        }
}
...
content = SomeHelperClass.Result<string>(
  () => response.Content.ReadAsStringAsync().Result
  );

-3

これらのウィンドウ非同期メソッドには、AsTask()と呼ばれる気の利いた小さなメソッドがあります。これを使用して、メソッドがそれ自体をタスクとして返すようにして、Wait()を手動で呼び出すことができます。

たとえば、Windows Phone 8 Silverlightアプリケーションでは、次のことができます。

private void DeleteSynchronous(string path)
{
    StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
    t.Wait();
}

private void FunctionThatNeedsToBeSynchronous()
{
    // Do some work here
    // ....

    // Delete something in storage synchronously
    DeleteSynchronous("pathGoesHere");

    // Do other work here 
    // .....
}

お役に立てれば!


-4

同期したい場合

MethodAsync().RunSynchronously()

3
このメソッドは、コールドタスクを開始するためのものです。通常、非同期メソッドは、ホットタスク、つまり既に開始されているタスクを返します。呼び出しRunSynchronously()に熱いタスク結果にInvalidOperationException。次のコードでお試しください:Task.Run(() => {}).RunSynchronously();
Theodor Zoulias

-5
   //Example from non UI thread -    
   private void SaveAssetAsDraft()
    {
        SaveAssetDataAsDraft();
    }
    private async Task<bool> SaveAssetDataAsDraft()
    {
       var id = await _assetServiceManager.SavePendingAssetAsDraft();
       return true;   
    }
   //UI Thread - 
   var result = Task.Run(() => SaveAssetDataAsDraft().Result).Result;

2
デッドロックを生成します。答えを削除するほうがよい。
PreguntonCojoneroCabrón

Task.Run(()=> SaveAssetDataAsDraft())。Result; -デッドロックを生成しません
アヌビス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.