非同期Task <T>メソッドを同期的に実行するにはどうすればよいですか?

667
Rachel 2011-02-24 08:18.

私はasync / awaitについて学んでいて、非同期メソッドを同期的に呼び出す必要がある状況に遭遇しました。どうやってやるの?

非同期メソッド:

public async Task<Customers> GetCustomers()
{
    return await Service.GetCustomersAsync();
}

通常の使用法:

public async void GetCustomers()
{
    customerList = await GetCustomers();
}

私は以下を使ってみました:

Task<Customer> task = GetCustomers();
task.Wait()

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)

ここからも提案を試みましたが、ディスパッチャが一時停止状態の場合は機能しません。

public static void WaitWithPumping(this Task task) 
{
        if (task == null) throw new ArgumentNullException(“task”);
        var nestedFrame = new DispatcherFrame();
        task.ContinueWith(_ => nestedFrame.Continue = false);
        Dispatcher.PushFrame(nestedFrame);
        task.Wait();
}

呼び出しによる例外とスタックトレースはRunSynchronously次のとおりです。

System.InvalidOperationException

メッセージ:デリゲートにバインドされていないタスクでRunSynchronouslyを呼び出すことはできません。

InnerException:null

ソース:mscorlib

StackTrace

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

20 answers

468
Rachel 2011-02-24 11:02.

これは、すべてのケース(一時停止されたディスパッチャを含む)で機能することがわかった回避策です。これは私のコードではなく、完全に理解するためにまだ取り組んでいますが、機能します。

次を使用して呼び出すことができます。

customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());

コードはここからです

public static class AsyncHelpers
{
    /// <summary>
    /// Execute's an async Task<T> method which has a void return value synchronously
    /// </summary>
    /// <param name="task">Task<T> method to execute</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        synch.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Execute's an async Task<T> method which has a T return type synchronously
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        T ret = default(T);
        synch.Post(async _ =>
        {
            try
            {
                ret = await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();
        SynchronizationContext.SetSynchronizationContext(oldContext);
        return ret;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private bool done;
        public Exception InnerException { get; set; }
        readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();

        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("We cannot send to our same thread");
        }

        public override void Post(SendOrPostCallback d, object state)
        {
            lock (items)
            {
                items.Enqueue(Tuple.Create(d, state));
            }
            workItemsWaiting.Set();
        }

        public void EndMessageLoop()
        {
            Post(_ => done = true, null);
        }

        public void BeginMessageLoop()
        {
            while (!done)
            {
                Tuple<SendOrPostCallback, object> task = null;
                lock (items)
                {
                    if (items.Count > 0)
                    {
                        task = items.Dequeue();
                    }
                }
                if (task != null)
                {
                    task.Item1(task.Item2);
                    if (InnerException != null) // the method threw an exeption
                    {
                        throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                    }
                }
                else
                {
                    workItemsWaiting.WaitOne();
                }
            }
        }

        public override SynchronizationContext CreateCopy()
        {
            return this;
        }
    }
}
330
AK_ 2012-08-03 07:03.

助言されるこの回答は3歳です。私は主に.Net4.0での経験に基づいて作成しましたが、4.5では特にasync-await。一般的に言って、それは素晴らしく単純な解決策ですが、時々物事を壊します。コメントの議論を読んでください。

.Net 4.5

これを使用するだけです:

// For Task<T>: will block until the task is completed...
var result = task.Result; 

// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();

参照:TaskAwaiter、Task.Result、Task.RunSynchronously


.Net 4.0

これを使って:

var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

...またはこれ:

task.Start();
task.Wait();
136
James Ko 2016-02-08 09:29.

誰もこれについて言及していないことに驚いた:

public Task<int> BlahAsync()
{
    // ...
}

int result = BlahAsync().GetAwaiter().GetResult();

ここにある他のいくつかの方法ほどきれいではありませんが、次の利点があります。

  • 例外を飲み込まない(のようなWait
  • AggregateException(のようなResult)でスローされた例外をラップしません
  • 両方のための作品TaskTask<T>(自分でそれを試してみます!)

また、GetAwaiterはダックタイプであるため、これは、タスクだけでなく、非同期メソッド(ConfiguredAwaitableまたはなどYieldAwaitable)から返されるすべてのオブジェクトに対して機能するはずです。


編集:このアプローチ(またはを使用.Result)では、(直接呼び出す.ConfigureAwait(false)メソッドBlahAsync()だけでなく)到達できる可能性のあるすべての非同期メソッドについて、待機するたびに必ず追加しない限り、デッドロックが発生する可能性があることに注意してください。説明。

// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
                                        // all its descendants use ConfigureAwait(false)
                                        // too. Then you can be sure that
                                        // BlahAsync().GetAwaiter().GetResult()
                                        // won't deadlock.

.ConfigureAwait(false)どこにでも追加するのが面倒で、パフォーマンスを気にしない場合は、代わりに行うことができます

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
74
Michael L Perry 2013-06-14 08:50.

スケジューラーをだまして同期的に実行するよりも、スレッドプールでタスクを実行する方がはるかに簡単です。そうすれば、デッドロックが発生しないことを確認できます。コンテキストスイッチが原因でパフォーマンスが影響を受けます。

Task<MyResult> DoSomethingAsync() { ... }

// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());

// Will block until the task is completed...
MyResult result = task.Result; 
63
Stephen Cleary 2016-09-08 03:40.

私はasync / awaitについて学んでいて、非同期メソッドを同期的に呼び出す必要がある状況に遭遇しました。どうやってやるの?

最善の答えは、あなたがそうしないことです。詳細は「状況」が何であるかに依存します。

プロパティゲッター/セッターですか?ほとんどの場合、「非同期プロパティ」よりも非同期メソッドを使用する方が適切です。(詳細については、非同期プロパティに関する私のブログ投稿を参照してください)。

これはMVVMアプリであり、非同期データバインディングを実行したいですか?次に、非同期データバインディングに関するMSDNの記事でNotifyTask説明されているように、私のようなものを使用します。

コンストラクターですか?次に、非同期ファクトリメソッドを検討することをお勧めします。(詳細については、非同期コンストラクターに関する私のブログ投稿を参照してください)。

ほとんどの場合、sync-over-asyncを実行するよりも良い答えがあります。

あなたの状況でそれが不可能な場合(そしてあなたがここで状況を説明する質問をすることでこれを知っている)、私は同期コードを使用することをお勧めします。ずっと非同期が最適です。ずっと同期するのが2番目に良いです。Sync-over-asyncはお勧めしません。

ただし、sync-over-asyncが必要な状況がいくつかあります。具体的には、呼び出し元のコードに制約されるため、同期する必要があり(非同期を許可するためにコードを再考または再構築する方法はまったくありません)、非同期コードを呼び出す必要あります。これは非常にまれな状況ですが、時々発生します。

その場合、ブラウンフィールドasync開発に関する私の記事で説明されているハックの1つを使用する必要があります。具体的には次のとおりです。

  • ブロッキング(例GetAwaiter().GetResult())。これによりデッドロックが発生する可能性があることに注意してください(ブログで説明しています)。
  • スレッドプールスレッドでコードを実行する(例:)Task.Run(..).GetAwaiter().GetResult()。これは、非同期コードをスレッドプールスレッドで実行できる場合にのみ機能することに注意してください(つまり、UIまたはASP.NETコンテキストに依存しません)。
  • ネストされたメッセージループ。これは、非同期コードが特定のコンテキストタイプではなく、シングルスレッドコンテキストのみを想定している場合にのみ機能することに注意してください(多くのUIおよびASP.NETコードは特定のコンテキストを想定しています)。

ネストされたメッセージループは、再入可能性を引き起こすため、すべてのハッキングの中で最も危険です。再入可能性について推論するのは非常に困難であり、(IMO)はWindows上のほとんどのアプリケーションバグの原因です。特に、UIスレッドを使用していて、作業キューでブロックしている場合(非同期作業が完了するのを待っている場合)、CLRは実際にメッセージポンピングを実行します-実際には、内部からいくつかのWin32メッセージを処理しますコード。ああ、そしてあなたはどのメッセージがわからない-クリス・ブルム が「何が汲み上げられるかを正確に知ることは素晴らしいことではないだろうか?残念ながら、汲み上げは人間の理解を超えた黒い芸術である」。、それなら私たちは本当に知る望みはありません。

したがって、UIスレッドでこのようにブロックすると、問題が発生します。同じ記事からの別のcbrummeの引用:「社内外の顧客は、STA [UIスレッド]での管理されたブロック中にメッセージを送信していることに気付くことがあります。これは非常に難しいことを知っているため、正当な懸念事項です。再入可能性に直面しても堅牢なコードを書くこと。」

はい、そうです。再入可能性に直面しても堅牢なコードを書くのは非常に困難です。そして、ネストされたメッセージループが強制あなたが再入の顔で堅牢なのコードを記述すること。これが、この質問に対して受け入れられた(そして最も支持された)回答が実際には非常に危険である理由です。

他のすべてのオプションを完全に使用できない場合(コードを再設計できない、非同期になるように再構築できない)、変更できない呼び出しコードによって同期を強制される、ダウンストリームコードを同期するように変更することはできません-ブロックすることはできません-非同期コードを別のスレッドで実行することはできません-その場合にのみ、再入可能性を採用することを検討する必要があります。

あなたは、このコーナーで自分自身を見つけるした場合は、私のようなものを使用することをお勧めしますDispatcher.PushFrameWPFのアプリのためにループ、Application.DoEventsのWinFormアプリケーションのための、および一般的なケースのために、私自身AsyncContext.Run

25
Theo Yaung 2011-02-25 11:29.

私があなたの質問を正しく読んでいる場合-非同期メソッドへの同期呼び出しを必要とするコードは、中断されたディスパッチャースレッドで実行されています。そして、asyncメソッドが完了するまで、実際にそのスレッドを同期的にブロックしたいとします。

C#5の非同期メソッドは、メソッドをTask内部で効果的に細かく切り刻み、シャバン全体の全体的な完了を追跡できるaを返すことで強化されています。ただし、切り刻まれたメソッドの実行方法は、awaitオペレーターに渡される式のタイプによって異なります。

ほとんどの場合await、型の式で使用しますTask。タスクのawaitパターンの実装は、に準拠しているという点で「スマート」ですSynchronizationContext。これにより、基本的に次のことが発生します。

  1. に入るスレッドawaitがDispatcherまたはWinFormsメッセージループスレッド上にある場合、asyncメソッドのチャンクがメッセージキューの処理の一部として発生することを保証します。
  2. に入るawaitスレッドがスレッドプールスレッド上にある場合、asyncメソッドの残りのチャンクはスレッドプールのどこかに発生します。

そのため、おそらく問題が発生しています。非同期メソッドの実装は、中断されていても、残りをDispatcherで実行しようとしています。

.... バックアップ!...。

私は質問をしなければなりません、なぜあなたは非同期メソッドで同期的にブロックしようとしているのですか?そうすることは、メソッドが非同期的に呼び出されることを望んでいた理由の目的を無効にします。一般awaitに、ディスパッチャーまたはUIメソッドで使用を開始するときは、UIフロー全体を非同期にする必要があります。たとえば、コールスタックが次のようなものだった場合:

  1. [上] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()-WPFまたはWinFormsコード
  6. [メッセージループ] -WPFまたはWinFormsメッセージループ

次に、コードが非同期を使用するように変換されると、通常は次のようになります。

  1. [上] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()-WPFまたはWinFormsコード
  6. [メッセージループ] -WPFまたはWinFormsメッセージループ

実際に答える

上記のAsyncHelpersクラスは、ネストされたメッセージループのように動作するため実際に機能しますが、Dispatcher自体で実行しようとするのではなく、独自の並列メカニズムをDispatcherにインストールします。これが問題の回避策の1つです。

別の回避策は、スレッドプールスレッドで非同期メソッドを実行し、それが完了するのを待つことです。これを行うのは簡単です-次のスニペットでそれを行うことができます:

var customerList = TaskEx.RunEx(GetCustomers).Result;

最終的なAPIはTask.Run(...)になりますが、CTPではExサフィックスが必要になります(ここで説明します)。

23
Clement 2014-07-23 19:42.

これは私にとってうまく機能しています

public static class TaskHelper
{
    public static void RunTaskSynchronously(this Task t)
    {
        var task = Task.Run(async () => await t);
        task.Wait();
    }

    public static T RunTaskSynchronously<T>(this Task<T> t)
    {
        T res = default(T);
        var task = Task.Run(async () => res = await t);
        task.Wait();
        return res;
    }
}
17
J. Lennon 2013-10-05 16:54.

主に単体テストやWindowsサービスの開発で、何度か直面しました。現在、私は常にこの機能を使用しています。

        var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
        {
            Trace.WriteLine("Task runSync Start");
            await TaskEx.Delay(2000); // Simulates a method that returns a task and
                                      // inside it is possible that there
                                      // async keywords or anothers tasks
            Trace.WriteLine("Task runSync Completed");
        })).Unwrap();
        Trace.WriteLine("Before runSync Wait");
        runSync.Wait();
        Trace.WriteLine("After runSync Waited");

シンプルで簡単、問題ありませんでした。

17
pixel 2016-10-26 13:22.

UIスレッドをブロックせずにタスクを同期的に実行するために私が見つけた最も簡単な方法は、次のようにRunSynchronously()を使用することです。

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();

私の場合、何かが発生したときに発生するイベントがあります。何回発生するかわかりません。したがって、イベントでは上記のコードを使用するので、起動するたびにタスクが作成されます。タスクは同期して実行され、私にとってはうまく機能します。とてもシンプルなことを考えると、これを見つけるのにとても時間がかかったのには驚きました。通常、推奨事項ははるかに複雑でエラーが発生しやすくなります。これはシンプルでクリーンだった。

16
wenhx 2014-12-02 16:06.

このコードはMicrosoft.AspNet.Identity.Coreコンポーネントで見つかりましたが、機能します。

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

// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
    CultureInfo cultureUi = CultureInfo.CurrentUICulture;
    CultureInfo culture = CultureInfo.CurrentCulture;
    return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = cultureUi;
        return func();
    }).Unwrap<TResult>().GetAwaiter().GetResult();
}
13
RredCat 2012-06-21 12:10.

ちょっとした注意-このアプローチ:

Task<Customer> task = GetCustomers();
task.Wait()

WinRTで動作します。

説明させてください:

private void TestMethod()
{
    Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
    task.Wait(); // wait executing the method
    var customer = task.Result; // get's result.
    Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
    public Customer()
    {
        new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
    }
    public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
    return Task.Run(() => new Customer
    {
        Name = "MyName"
    });
}

さらに、このアプローチはWindowsストアソリューションでのみ機能します。

注:他の非同期メソッド内でメソッドを呼び出す場合、この方法はスレッドセーフではありません(@Servyのコメントによる)

11
Liang 2018-10-24 23:08.

.Net4.6でテスト済み。また、デッドロックを回避することもできます。

を返す非同期メソッドの場合Task

Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();

非同期メソッドを返す場合 Task<T>

Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;

編集

呼び出し元がスレッドプールスレッドで実行されている場合(または呼び出し元もタスク内にある場合)、状況によってはデッドロックが発生する可能性があります。

9
Dan Abramov 2011-02-24 08:23.

コードでは、タスクが実行されるのを最初に待機しますが、タスクを開始していないため、無期限に待機します。これを試して:

Task<Customer> task = GetCustomers();
task.RunSynchronously();

編集:

あなたは例外を得ると言います。スタックトレースなど、詳細を投稿してください。
Monoには、次のテストケースが含まれています。

[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

これがうまくいくかどうかを確認してください。そうでない場合は、非常にまれですが、AsyncCTPのビルドがおかしい可能性があります。それが機能する場合は、コンパイラーが正確に何を生成し、Taskインスタンス化がこのサンプルとどのように異なるかを調べることをお勧めします。

編集#2:

私はときに説明した例外が発生したことをリフレクターでチェックm_actionですnull。これはちょっと奇妙ですが、私は非同期CTPの専門家ではありません。私が言ったように、コードを逆コンパイルしてTask、どのようにしてインスタンス化されているかを正確に確認する必要m_actionがありnullます。

5
Daniel A. White 2011-02-24 08:20.

次のような呼び出しを作成してみませんか。

Service.GetCustomers();

それは非同期ではありません。

4
Mahesh 2016-10-19 18:13.

以下のコードスニップを使用してください

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
3
Contango 2014-09-13 07:41.

この回答は、WPF for .NET4.5を使用しているすべての人を対象としています。

Task.Run()GUIスレッドで実行しようとすると、関数定義にキーワードtask.Wait()がない場合、は無期限にハングasyncします。

この拡張メソッドは、GUIスレッドを使用しているかどうかを確認し、使用している場合はWPFディスパッチャースレッドでタスクを実行することで問題を解決します。

このクラスは、MVVMプロパティや、async / awaitを使用しない他のAPIへの依存関係など、避けられない状況で、async / awaitワールドとnon-async / awaitワールドの間の接着剤として機能できます。

/// <summary>
///     Intent: runs an async/await task synchronously. Designed for use with WPF.
///     Normally, under WPF, if task.Wait() is executed on the GUI thread without async
///     in the function signature, it will hang with a threading deadlock, this class 
///     solves that problem.
/// </summary>
public static class TaskHelper
{
    public static void MyRunTaskSynchronously(this Task task)
    {
        if (MyIfWpfDispatcherThread)
        {
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E99213. Task did not run to completion.");
            }
        }
        else
        {
            task.Wait();
            if (task.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E33213. Task did not run to completion.");
            }
        }
    }

    public static T MyRunTaskSynchronously<T>(this Task<T> task)
    {       
        if (MyIfWpfDispatcherThread)
        {
            T res = default(T);
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E89213. Task did not run to completion.");
            }
            return res;
        }
        else
        {
            T res = default(T);
            var result = Task.Run(async () => res = await task);
            result.Wait();
            if (result.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E12823. Task did not run to completion.");
            }
            return res;
        }
    }

    /// <summary>
    ///     If the task is running on the WPF dispatcher thread.
    /// </summary>
    public static bool MyIfWpfDispatcherThread
    {
        get
        {
            return Application.Current.Dispatcher.CheckAccess();
        }
    }
}
2
donttellya 2016-02-16 23:58.

次のヘルパーメソッドでも問題を解決できると思います。

private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
    {
        TResult result = default(TResult);
        var autoResetEvent = new AutoResetEvent(false);

        Task.Run(async () =>
        {
            try
            {
                result = await func();
            }
            catch (Exception exc)
            {
                mErrorLogger.LogError(exc.ToString());
            }
            finally
            {
                autoResetEvent.Set();
            }
        });
        autoResetEvent.WaitOne();

        return result;
    }

次のように使用できます。

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
2
Ogglas 2017-09-26 03:49.

多くの人がコメントで言っているように、単に電話をかける.Result;.Wait()、デッドロックのリスクがあります。私たちのほとんどはワンライナーが好きなので、これらを使用することができます.Net 4.5<

非同期メソッドを介して値を取得する:

var result = Task.Run(() => asyncGetValue()).Result;

非同期メソッドを同期的に呼び出す

Task.Run(() => asyncMethod()).Wait();

の使用によるデッドロックの問題は発生しませんTask.Run

ソース:

https://stackoverflow.com/a/32429753/3850405

1
Dan Nguyen 2018-07-28 05:20.

これは私のために働く

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

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

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

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

    class SomeClass
    {
        public async Task<object> LoginAsync(object loginInfo)
        {
            return await Task.FromResult(0);
        }
        public object Login(object loginInfo)
        {
            return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
            //return this.LoginAsync(loginInfo).Result.Content;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var someClass = new SomeClass();

            Console.WriteLine(someClass.Login(1));
            Console.ReadLine();
        }
    }
}
0
Jaider 2020-11-17 10:34.

注:非同期の場合、アクションの性質を変更することはベストプラクティスとして推奨されていないと思います。最善の方法は、そのまま処理することです(完全に非同期)。このようにして、並列処理/マルチスレッドなどの他の利点を得ることができます。

他の回答がこのアプローチを使用しなかったのを見て、私もここに投稿したいと思います:

var customers = GetCustomersAsync().GetAwaiter().GetResult();

Related questions

MORE COOL STUFF

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

グッドジョブ、ESPN

グッドジョブ、ESPN

今日のホワイトハウスの記者会見で、報道官のサラ・ハッカビー・サンダースは、スポーツセンターのホストであるイェメル・ヒルのドナルド・トランプに関する最近のツイートについてコメントするよう求められ、大統領と彼の政策を白人至上主義者として説明した。ヒルは彼女のつぶやきのためにESPNによって公に叱責されました。

アマゾンからのこのレミントンツールセールで髪を整える、スタイルを整える、乾かす、または取り除く

アマゾンからのこのレミントンツールセールで髪を整える、スタイルを整える、乾かす、または取り除く

基本的に頭のあらゆる部分から髪の毛を整えたり、ブロードライしたり、まっすぐにしたり、脱毛したりする必要がある場合は、このレミントンゴールドボックスが最適です。今日だけ、Amazonは、すでに人気のあるShortcut Pro Self-HaircutKitやPearlPro Ceramic Flat Ironのように、グルーミングをはるかに簡単にするヘアツールをマークダウンしています。

カナダの元桂冠詩人が、史上最高の文化の盗用でトゥパックとマヤアンジェロウから盗んだ

カナダの元桂冠詩人が、史上最高の文化の盗用でトゥパックとマヤアンジェロウから盗んだ

トゥパックシャクール(ティムモーゼンフェルダー/ゲッティイメージズ); マヤアンジェロウ(マーティンゴッドウィン/ゲッティイメージズ)移動、テイラースウィフト。ケンドールとカイリーは、必要な数の座席を持っています。

テスラは、ハリケーンイルマによる避難を容易にするために、フロリダでの車両の範囲を拡大しています

テスラは、ハリケーンイルマによる避難を容易にするために、フロリダでの車両の範囲を拡大しています

写真:Tesla Motorsフロリダに住んでいて、Tesla Model S、Model X 60、またはModel 60Dを使用している場合、車の自律性は50 km(約30マイル)高くなります。これは失敗ではなく、ハリケーンイルマによる避難作業を容易にするために会社自身が命じた自治権の一時的な延長です。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

投資ノート:Bioscout AU$300万シード

投資ノート:Bioscout AU$300万シード

Bioscoutは、農家を運転席に置くという使命を負っています。Artesian(GrainInnovate)やUniseedと並んで、最新のシードラウンドでチームを支援できることをうれしく思います。問題真菌症による重大な作物の損失は、農民にとって試練であることが証明されています。

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

遠隔医療は、パンデミック後の時代では新しいものではなく、時代遅れの分野でもありません。しかし、業界を詳しく見ると、需要と供給の強力な持続可能性と、米国で絶え間ない革命となる強力な潜在的成長曲線を示しています。

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

2021年は、世界的なベンチャーキャピタル(VC)の資金調達にとって記録的な年でした。DealStreetAsiaによると、東南アジアも例外ではなく、この地域では年間で記録的な25の新しいユニコーンが採掘されました。

ムーアの法則を超えて

ムーアの法則を超えて

計算に対する私たちの欲求とムーアの法則が提供できるものとの間には、指数関数的に増大するギャップがあります。私たちの文明は計算に基づいています—建築と想像力の現在の限界を超える技術を見つけなければなりません。

Language