715

私は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()
4

26 に答える 26

513

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

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

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;
        }
    }
}
于 2011-02-23T21:02:36.343 に答える
350

この回答は 3 年前のものです。私は主に .Net 4.0 の経験に基づいて書きましたが、4.5、特に .NET の経験はほとんどありません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();

参照: TaskAwaiterTask.ResultTask.RunSynchronously


.Net 4.0

これを使って:

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

x.AsyncWaitHandle.WaitOne();

...またはこれ:

task.Start();
task.Wait();
于 2012-08-02T17:03:47.100 に答える
78

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

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; 
于 2013-06-13T18:50:54.853 に答える
69

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

詳細は「状況」によって異なります

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

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

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

ほとんどの場合、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 は実際にメッセージ ポンピングを行います。コード。ああ、そして、どのメッセージかはわかりません - Chris Brumme が「何が汲み上げられるかを正確に知ることができたら素晴らしいと思いませんか? 残念ながら、汲み上げは人間の理解を超えた黒魔術です。」、それなら、私たちは本当に知る望みはありません.

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

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

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

このコーナーにいる場合は、Dispatcher.PushFramefor WPF appsApplication.DoEventsfor WinForm apps 、および一般的なケースでは my own のようなものを使用することをお勧めしますAsyncContext.Run

于 2016-09-07T13:40:33.810 に答える
26

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

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

ほとんどの場合await、 type の式で使用しますTask。タスクのパターンの実装は、基本的に次のようなことが起こる原因となる に従ってawaitいるという点で「スマート」です。SynchronizationContext

  1. に入るスレッドがawaitDispatcher または WinForms メッセージ ループ スレッド上にある場合、非同期メソッドのチャンクがメッセージ キューの処理の一部として発生することが保証されます。
  2. に入るawaitスレッドがスレッド プール スレッド上にある場合、非同期メソッドの残りのチャンクはスレッド プールの任意の場所で発生します。

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

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

質問する必要があります。なぜ非同期メソッドで同期的にブロックしようとしているのですか? そうすることは、メソッドが非同期的に呼び出される理由の目的を無効にします。一般にawait、Dispatcher または 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 つです。

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

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

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

于 2011-02-24T21:29:15.890 に答える
23

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

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;
    }
}
于 2014-07-23T05:42:37.700 に答える
17

ほとんどの場合、単体テストまたは 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");

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

于 2013-10-05T02:54:50.803 に答える
17

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();
}
于 2014-12-02T02:06:48.140 に答える
13

ちょっとしたメモ - このアプローチ:

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のコメントによると)

于 2012-06-20T22:10:20.490 に答える
9

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

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);
}

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

編集#2:

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

于 2011-02-23T18:23:15.087 に答える
6

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

Service.GetCustomers();

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

于 2011-02-23T18:20:34.367 に答える
4

以下のコードスニップを使用

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
于 2016-10-19T04:13:30.440 に答える
3

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

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

この拡張メソッドは、GUI スレッド上にあるかどうかを確認し、そうであれば、WPF ディスパッチャー スレッドでタスクを実行することで問題を解決します。

このクラスは、MVVM プロパティや、async/await を使用しない他の API への依存関係など、避けられない状況で、async/await の世界と非 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();
        }
    }
}
于 2014-09-12T17:41:19.000 に答える
2

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

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);
于 2016-02-16T09:58:37.270 に答える
-2

これには SpinWait がかなりうまく機能することがわかりました。

var task = Task.Run(()=>DoSomethingAsyncronous());

if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
   return false;
}

return true;

上記のアプローチでは、.Result または .Wait() を使用する必要はありません。また、タイムアウトを指定できるため、タスクが完了しない場合に永久に立ち往生することはありません。

于 2019-02-06T22:02:41.097 に答える
-3

wp8の場合:

包んでください:

Task GetCustomersSynchronously()
{
    Task t = new Task(async () =>
    {
        myCustomers = await GetCustomers();
    }
    t.RunSynchronously();
}

あれを呼べ:

GetCustomersSynchronously();
于 2013-02-26T23:13:36.460 に答える