C # zaman uyumlu yöntemden zaman uyumsuz yöntemi nasıl çağırılır?


862

Sahibim public async void Foo()Eşzamanlı yöntemden aramak istediğim yöntem var. Şimdiye kadar MSDN belgelerinde gördüğüm tek şey, zaman uyumsuz yöntemlerle zaman uyumsuz yöntemler çağırıyor, ama tüm programım zaman uyumsuz yöntemlerle inşa edilmemiştir.

Bu mümkün mü?

Bu yöntemleri eşzamansız bir yöntemle çağırmanın bir örneği: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx

Şimdi bu asenkron yöntemleri senkronizasyon yöntemlerinden çağırıyorum.


2
Ben de bununla karşılaştım. Bir RoleProvider'ı geçersiz kılmak, GetRolesForUser yönteminin yöntem imzasını değiştiremezsiniz, böylece yöntemi zaman uyumsuz hale getiremezsiniz ve bu nedenle api'yi eşzamansız olarak çağırmak için await komutunu kullanamazsınız. Geçici çözümüm, genel HttpClient sınıfıma senkronize yöntemler eklemekti, ancak bunun mümkün olup olmadığını (ve etkilerinin ne olabileceğini) bilmek istiyorum.
Timothy Lee Russell

1
Metodunuz async void Foo()bir döndürmediği için Task, arayanın tamamladığını bilemeyeceği anlamına gelir, bunun Taskyerine geri dönmesi gerekir .
Dai

1
Bir UI iş parçacığında bunun nasıl yapılacağı ile ilgili bir q / a bağlantısı .
noseratio

Yanıtlar:


711

Zaman uyumsuz programlama kod tabanı üzerinden "büyür". Bu edilmiş bir zombi virüsü ile karşılaştırıldığında . En iyi çözüm büyümesine izin vermektir, ancak bazen bu mümkün değildir.

Ben Nito.AsyncEx benim birkaç tür yazdımKısmen eşzamansız kod tabanı ile uğraşmak için kütüphanemde . Yine de her durumda çalışan bir çözüm yok.

Çözüm A

Bağlamına geri senkronize edilmesi gerekmeyen basit bir eşzamansız yönteminiz varsa, şunları kullanabilirsiniz Task.WaitAndUnwrapException:

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

Sen do not kullanmak istediğiniz Task.Waitveya Task.Resultonlar istisnalar sarın çünkü AggregateException.

Bu çözüm yalnızca MyAsyncMethodiçeriğiyle senkronize edilmezse uygundur . Bir başka deyişle, her awaityer MyAsyncMethodbitmelidir ConfigureAwait(false). Bu, herhangi bir UI öğesini güncelleyemeyeceği veya ASP.NET istek içeriğine erişemeyeceği anlamına gelir.

Çözüm B

Eğer MyAsyncMethodkendi bağlamına senkronize arkasına ihtiyacı da artar, ardından kullanmak mümkün olabilir AsyncContext.RunTaskyuvalanmış bir bağlam sağlamak için:

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

* Güncelleme 4/14/2014: Kütüphanenin daha yeni sürümlerinde API aşağıdaki gibidir:

var result = AsyncContext.Run(MyAsyncMethod);

( Task.ResultBu örnekte kullanmak uygun , çünkü RunTaskyayılacakTask İstisnalar ).

Bunun AsyncContext.RunTaskyerine ihtiyaç duymanızın nedeni Task.WaitAndUnwrapException, WinForms / WPF / SL / ASP.NET'te gerçekleşen oldukça ince bir kilitlenme olasılığıdır:

  1. Eşzamanlı bir yöntem, a Task.
  2. Eşzamanlı yöntem, üzerinde engelleme beklemesi yapar Task.
  3. asyncYöntemini kullanır awaitolmadan ConfigureAwait.
  4. TaskZaman sadece tamamlar, çünkü bu durumda tamamlayamıyor asyncyöntem bitmiş olduğu; asyncyöntem tam değil onun devamını planlamak için çalışıyor çünkü SynchronizationContext, ve WinForms / WPF / SL / ASP.NET senkron yöntem zaten bu bağlamda çalışıyor çünkü devamı çalışmasına izin vermeyecektir.

Bu, ConfigureAwait(false)her asyncyöntemde mümkün olduğunca kullanılmasının iyi bir fikir olmasının bir nedenidir .

Çözüm C

AsyncContext.RunTaskher senaryoda çalışmaz. Örneğin, asyncyöntem tamamlanması için bir UI olayı gerektiren bir şey beklerse, iç içe bağlamda bile kilitleneceksiniz. Bu durumda, asyncyöntemi thread havuzunda başlatabilirsiniz :

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

Ancak, bu çözüm MyAsyncMethodiş parçacığı havuzu bağlamında çalışacak bir gerektirir . Bu yüzden UI öğelerini güncelleyemez veya ASP.NET istek içeriğine erişemez. Ve bu durumda, siz de ekleyebilirsiniz ConfigureAwait(false)için onunawait tabloların ve çözüm A'yı kullanmak

Güncelleme, 2019-05-01: Geçerli "en kötü uygulamalar" burada bir MSDN makalesinde bulunmaktadır .


9
Çözüm A istediğim gibi görünüyor, ancak task.WaitAndUnwrapException () .Net 4.5 RC içine yapmadı; yalnızca task.Wait () işlevine sahiptir. Bunu yeni sürümle nasıl yapacağınız hakkında bir fikriniz var mı? Yoksa yazdığınız özel bir uzantı yöntemi mi?
deadlydog

3
WaitAndUnwrapExceptionAsyncEx kütüphanemdeki kendi yöntemim . Resmi .NET kütüphaneleri, senkronizasyon ve zaman uyumsuz kodları karıştırmak için fazla yardım sağlamaz (ve genel olarak bunu yapmamalısınız!). AsyncEx'i 4.5 üzerinde çalışacak şekilde güncellemeden önce .NET 4.5 RTW ve yeni bir XP olmayan dizüstü bilgisayar bekliyorum (XP'de birkaç hafta daha takılı kaldığım için şu anda 4.5 için geliştiremiyorum).
Stephen Cleary

12
AsyncContextşimdi Runbir lambda ifadesi alan bir yöntem var, bu yüzden kullanmalısınızvar result = AsyncContext.Run(() => MyAsyncMethod());
Stephen Cleary

1
Kütüphanenizi Nuget'ten çıkardım, ama aslında bir RunTaskyöntemi yok gibi görünüyor . Bulabildiğim en yakın şey Run, ama bir Resultözelliği yok.
Asad Saeeduddin

3
@Asad: Evet, 2 yıldan fazla bir süre sonra API değişti. Şimdi basitçe söyleyebilirimvar result = AsyncContext.Run(MyAsyncMethod);
Stephen Cleary

313

Sonunda sorunumu çözen bir çözüm eklemek, umarım birinin zamanından tasarruf etmesini sağlar.

Önce Stephen Cleary'nin birkaç makalesini okuyun :

"Eşzamansız Kodda Engelleme" bölümündeki "en iyi iki uygulama" dan birincisi benim için çalışmadı ve ikincisi uygulanamazdı (temelde kullanabilirsem await, kullanıyorum !).

İşte benim geçici çözüm: çağrıyı bir sarın Task.Run<>(async () => await FunctionAsync());ve umarım artık kilitlenmez .

İşte benim kod:

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
İki yıl sonra, bu çözümün nasıl devam ettiğini merak ediyorum. Haber var mı? Yeni başlayanlarda kaybolan bu yaklaşımın inceliği var mı?
Dan Esparza

26
Bu kilitlenmeyecek, doğru değil, ancak yeni bir iş parçacığında, kaynak iş parçacığının eşitleme bağlamının dışında çalışmaya zorlandığı için. Bununla birlikte, bunun çok kötü tavsiye edildiği belirli ortamlar vardır: özellikle web uygulamaları. Bu, web sunucusu için kullanılabilir iş parçacıklarını yarıya indirebilir (istek için bir iş parçacığı ve bunun için bir iş parçacığı). Bunu ne kadar çok yaparsanız, o kadar kötü olur. Potansiyel olarak tüm web sunucunuzda kilitlenme meydana gelebilir.
Chris Pratt

30
@ChrisPratt - Haklı olabilirsiniz, çünkü Task.Run()zaman uyumsuz bir kodda en iyi uygulama değildir. Ama yine de, asıl sorunun cevabı nedir? Asla senkronize olmayan bir yöntem çağırma? Biz diliyoruz, ama gerçek bir dünyada, bazen yapmak zorundayız.
17'de Tohid

1
@ Stephen Cleary'nin kütüphanesini deneyebilirsin. İnsanların bunu varsaydığını gördüm ve Parallel.ForEachkötüye kullanımın 'gerçek dünyada' bir etkisi olmayacak ve sonunda sunucuları çökertti. Bu kod Konsol uygulamaları için uygundur, ancak @ChrisPratt'ın dediği gibi Web Uygulamalarında kullanılmamalıdır. "Şimdi" çalışabilir, ancak ölçeklenebilir değildir.
makhdumi

1
Yeni bir hesap oluşturmaya başlamak istiyorum, sadece bu oyu yükseltmek için yeterli puan almak için soruları cevaplamak ....
Giannis Paraskevopoulos

206

Microsoft, Async'i Sync olarak çalıştırmak için bir AsyncHelper (dahili) sınıfı oluşturdu. Kaynak şöyle görünür:

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 temel sınıflarının yalnızca Async yöntemleri vardır ve bunları Sync olarak adlandırmak için, uzantı yöntemlerine sahip sınıflar vardır (örnek kullanım):

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

Kodun lisans şartları hakkında endişe duyanlar için, Microsoft tarafından MIT Lisanslı olduğunu gösteren yorumlar içeren çok benzer bir koda (iş parçacığındaki kültür için destek ekler) bir bağlantı. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


2
Eşzamansız yöntemlerim diğer eşzamansız yöntemleri bekliyor. Hiçbir awaitçağrı ile süslemiyorum ConfigureAwait(false). Global.asax işlevinden AsyncHelper.RunSyncbir zaman uyumsuz işlevi çağırmak için kullanmayı denedim Application_Start()ve işe yarıyor gibi görünüyor. Bu, bu AsyncHelper.RunSyncgönderide başka bir yerde okuduğum "arayanın bağlamına geri dönme" kilitlenme sorununa güvenilir bir şekilde eğilimli olmadığı anlamına mı geliyor ?
Bob.at.Indigo.Health

1
@ Bob.at.SBS kodunuzun ne yaptığına bağlıdır. Bu kodu sanki güvenli miyim gibi basit değil . Bu, zaman uyumsuz komutları eşzamanlı olarak çalıştırmanın çok minimal ve yarı güvenli bir yoludur, kilitlenmelere neden olmak için uygun olmayan bir şekilde kolayca kullanılabilir.
Erik Philips

1
Teşekkürler. 2 takip sorusu: 1) Asenkron yönteminin bir kilitlenmeye neden olmasını önlemek istediği bir şeye örnek verebilir misiniz ve 2) bu bağlamda kilitlenmeler genellikle zamanlamaya bağlı mı? Eğer pratikte çalışıyorsa, hala kodumda gizlenen zamanlamaya bağlı bir kilitlenme olabilir mi?
Bob.at.Indigo.Health

@ Bob.at.SBS Sağ üstteki Soru Sor düğmesini kullanarak soru sormanızı tavsiye ederim . Referans olarak bu soruya veya cevabına bir bağlantı ekleyebilirsiniz.
Erik Philips

1
@ Bob.at ... Erik tarafından sağlanan kod Asp altında mükemmel çalışır. net mvc5 ve EF6, ancak web çözümlerimi tamamen askıya alan diğer çözümlerden (ConfigureAwait (false) .GetAwaiter (). GetResult () veya .result) herhangi birini denediğimde değil
LeonardoX

150

async Main artık C # 7.2'nin bir parçasıdır ve projelerin gelişmiş derleme ayarlarında etkinleştirilebilir.

C # <7.2 için doğru yol:

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


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

Bunun birçok Microsoft belgesinde kullanıldığını göreceksiniz, örneğin: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use- konular-abonelikleri


11
Birinin bunu neden oyladığını bilmiyorum. Bu benim için harika çalıştı. Bu düzeltme olmadan, HER YERDE ASYCH yaymak zorunda kalacaktı.
Mahkum ZERO

11
Bu neden daha iyi MainAsync().Wait()?
ezmek

8
Katılıyorum. Tüm bunlar yerine sadece MainAsync ().
Hacat

8
@crush Bunun bazı kilitlenmeleri nasıl önleyebileceğini anlatıyordum. Bazı durumlarda bir UI veya asp.net iş parçacığından .Wait () çağrısı kilitlenmeye neden olur. async deadlocks
David

6
@ClintB: Bunu kesinlikle ASP.NET Core'da yapmamalısınız. Web uygulamaları, iş parçacığı açlığına karşı özellikle savunmasızdır ve bunu her yaptığınızda havuzdan bir istek sunmak için kullanılacak bir iş parçacığını çekersiniz. Geleneksel olarak tek kullanıcı oldukları için masaüstü / mobil uygulamalar için daha az sorunludur.
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' anahtar kelimesini "bu uzun süren görevi başlat, sonra denetimi çağıran yönteme döndür" şeklinde okudunuz. Uzun süren görev tamamlandıktan sonra kodu bundan sonra yürütür. Beklemeden sonraki kod, CallBack yöntemleri olarak kullanılana benzer. Mantıksal akış olması arasındaki en büyük fark, yazmayı ve okumayı çok daha kolay hale getiren kesintiye uğramaz.


15
Waitistisnaları sarar ve bir kilitlenme olasılığı vardır.
Stephen Cleary

Kullanmadan bir async yöntemi çağırdıysanız await, senkronize olarak çalıştırılacağını düşündüm . En azından benim için çalışıyor (aramadan myTask.Wait). Aslında, myTask.RunSynchronously()daha önce idam edilmiş olduğu için aramaya çalıştığımda bir istisna aldım !
huşu

2
Bu cevabı beğendim. Düzenleme için iyi yorumlar, küçük ve zarif. Katkıda bulunduğunuz için teşekkür ederiz! Ben hala öğrenme eşzamanlılık, bu yüzden her şey yardımcı olur :)
kayleeFrye_onDeck

2
Bu cevap bugün itibariyle hala geçerli mi? Sadece bir MVC Razor projesinde denedim ve uygulama sadece erişime bağlı .Result.
Gitti Kodlama

7
@TrueBlueAussie Bu senkronizasyon bağlamı kilitlenmesidir. Eşzamansız kodunuz senkronizasyon bağlamına geri döner, ancak o sırada Resultçağrı tarafından engellenir , bu yüzden oraya asla ulaşmaz. Ve Resultasla bitmez, çünkü Resultsonunu bekleyen birini beklemektedir , temelde: D
Luaan

40

% 100 emin değilim, ancak bu blogda açıklanan tekniğin birçok durumda çalışması gerektiğine inanıyorum :

Bu nedenle, task.GetAwaiter().GetResult()bu yayılma mantığını doğrudan çağırmak istiyorsanız kullanabilirsiniz .


6
Stephen Cleary'nin yukarıdaki cevabındaki A çözümü bu yöntemi kullanır. Bkz. WaitAndUnwrapException kaynağı.
orad

Aradığınız işlev geçersizse veya görevse GetResult () kullanmanız gerekir mi? Yani herhangi bir sonuç almak istemiyorsanız
batmaci

Evet, aksi takdirde görev tamamlanana kadar engellemez. Alternatif olarak GetAwaiter () işlevini çağırmak yerine GetResult () işlevini .Wait ()
NStuke adlı kişiyi

1
Bu "birçok koşul" kısmıdır. Genel iplik geçirme modeline ve kilitlenme riski olup olmadığını belirlemek için diğer ipliklerin ne yaptığına bağlıdır.
NStuke

24

Bununla birlikte, her durumda çalışan (neredeyse: yorumlara bakın) iyi bir çözüm vardır: geçici bir mesaj pompası (SynchronizationContext).

Arama iş parçacığı beklendiği gibi engellenir, ancak zaman uyumsuz işlevinden çağrılan tüm sürekliliklerin, arama iş parçacığında çalışan geçici SynchronizationContext (ileti pompası) ile birleştirileceklerinden kilitlenmemelerini sağlar.

Geçici mesaj pompası yardımcısının kodu:

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

Kullanımı:

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

Asenkron pompanın daha ayrıntılı açıklaması burada bulunabilir .



HttpContext.Current'i rastgele kaybedebileceğiniz için bu bir Asp.net senaryosunda çalışmaz.
Josh Mouch

12

Artık bu soruya dikkat eden herkese ...

Eğer içeri bakarsanız Microsoft.VisualStudio.Services.WebApidenilen bir sınıf var TaskExtensions. Bu sınıf içinde statik uzantı yöntemini göreceksinizTask.SyncResult() , görev geri dönene kadar iş parçacığını tamamen engelleyen .

Dahili olarak çağırır task.GetAwaiter().GetResult()ancak herhangi çalışmalarına aşırı oluyor oldukça basit olan asyncdönüş olduğunu yöntemle Task, Task<T>ya Task<HttpResponseMessage>... sözdizimsel şeker, bebek ... Babanın Tatlıyı aldım.

Görünüşe göre ...GetAwaiter().GetResult()async kodunu engelleme bağlamında yürütmenin MS-resmi yolu. Kullanım durumum için çok iyi çalışıyor gibi görünüyor.


3
Beni "tamamen bloklar gibi" yaptın.
Dawood ibn Kareem

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

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

Veya bunu kullanın:

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

6

Eşzamanlı koddan, awaitüzerinde gerekene kadar herhangi bir eşzamansız yöntemi çağırabilirsiniz , bu durumda da işaretlenmelidir async.

Birçok kişinin burada önerdiği gibi, senkronize yönteminizde ortaya çıkan görev için Wait () veya Sonucu çağırabilirsiniz, ancak daha sonra bu yöntemde async'in amacını yenen bir engelleme çağrısı ile sonuçlanırsınız.

Metodunuzu gerçekten yapamıyorsunuz asyncve senkronize yöntemi kilitlemek istemiyorsanız, görevdeki ContinueWith yöntemine parametre olarak geçirerek bir geri çağırma yöntemi kullanmanız gerekecek.


5
O zaman bu yöntem senkronize olarak çağrılmayacak mıydı?
Jeff Mercado

2
Anladığım kadarıyla soru, asenkron olmayan bir yöntemden bir async yöntemini çağırabileceğinizdi. Bu, asenkron yöntemini bloke edici bir şekilde çağırmak anlamına gelmez.
base2

Maalesef, "onlar da işaretlenmeli async" diye dikkatimi çektiğin şeyden uzaklaştırdı.
Jeff Mercado

Eşzamansızlığı gerçekten umursamıyorsam, bu şekilde çağırmak uygun mudur (ve Stephen Cleary'nin naggingini sürdürdüğü sarılmış istisnalarda çıkmazların olasılığı nedir?) Bazı test yöntemlerim var (senkronize olarak çalıştırılmalıdır) eşzamansız yöntemleri test eder. Devam etmeden önce sonucu beklemeliyim, böylece eşzamansız yöntemin sonucunu test edebilirim.
huşu

6

Çok geç kaldığımı biliyorum. Ama benim gibi birinin bunu düzgün, kolay bir şekilde ve başka bir kütüphaneye bağlı kalmadan çözmek istediği durumda.

Aşağıdaki bulundu kod parçasını gelen Ryan

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

o zaman böyle diyebilirsin

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

6
Bu tam olarak yukarıdaki cevaba benziyor bir şey eksik miyim
inlokesh

2

Saatlerce farklı yöntemleri denedikten sonra, az ya da çok başarılı bir şekilde bitirdim. Sonuç alırken bir kilitlenmeyle bitmez ve aynı zamanda sarılmış olanı değil, orijinal istisnayı alır ve atar.

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

  return task.Result;
}

Dönüş görevi ile çalışır.GetAwaiter (). GetResult ();
Per G

evet, ama orijinal istisna ne olacak?
Jiří Herník

Bence sonuç temelde .GetAwaiter () ile aynı olduğunu düşünüyorum. GetResult ()
Per G

-2

Yeni bir iş parçacığından çağrılabilir (iş parçacığı havuzundan DEĞİL!):

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

Bu windows zaman uyumsuz yöntemlerinin AsTask () adı verilen şık bir yöntemi vardır. Bu yöntem, yöntemin kendisini bir görev olarak döndürmesini sağlamak için kullanabilirsiniz, böylece Wait () öğesini el ile çağırabilirsiniz.

Örneğin, bir Windows Phone 8 Silverlight uygulamasında aşağıdakileri yapabilirsiniz:

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 
    // .....
}

Bu yardımcı olur umarım!


-4

Çalıştırmak istiyorsanız Senkronizasyon

MethodAsync().RunSynchronously()

3
Bu yöntem, soğuk işlere başlamak için tasarlanmıştır. Genellikle eşzamansız yöntemler etkin bir görevi, başka bir deyişle zaten başlatılmış bir görevi döndürür. RunSynchronously()sıcak bir görev çağrıldığında bir InvalidOperationException. Bu kod ile deneyin: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
Kilitlenme oluşturun. Cevabı daha iyi silin.
PreguntonCojoneroCabrón

Görev.Run (() => SaveAssetDataAsDraft ()) Sonuç; - kilitlenme oluşturmaz
Anubis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.