Zaman uyumsuz bir Görev <T> yöntemini eşzamanlı olarak nasıl çalıştırabilirim?


628

Async / await hakkında öğreniyorum ve eşzamanlı olarak bir async yöntemini çağırmak gereken bir durumla karşılaştım. Bunu nasıl yapabilirim?

Zaman uyumsuz yöntem:

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

Normal kullanım:

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

Aşağıdaki kullanarak denedim:

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

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

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

Ayrıca buradan bir öneri denedim , ancak dağıtım görevlisi askıya alınmış bir durumda olduğunda çalışmaz.

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

İşte çağıran istisna ve yığın izleme RunSynchronously:

system.xml.dll

İleti : Bir temsilci için bağlı olmayan bir görevde RunSynchronously çağrılamaz.

InnerException : null

Kaynak : 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()

46
"Zaman uyumsuz yöntemi nasıl eşzamanlı olarak çağırabilirim" sorusunun en iyi yanıtı "yapma" dır. Çalışmaya zorlamak için kesmek var , ama hepsinin çok ince tuzakları var. Bunun yerine, bunu yapmanız için "ihtiyaç duymanızı" sağlayan kodu yedekleyin ve düzeltin.
Stephen Cleary

57
@Stephen Cleary Kesinlikle katılıyorum, ancak bazen kodunuzun async / await kullanmayan bazı 3. taraf API'lara bağımlı olması gibi kaçınılmaz. Ek olarak, MVVM kullanılırken WPF özelliklerine bağlanırsa, özelliklerde desteklenmediği için async / await kullanmak tam anlamıyla imkansızdır.
Contango

3
@StephenCleary Her zaman değil. GeneXus'a aktarılacak bir DLL oluşturuyorum . Asenkron / bekliyor anahtar kelimeleri desteklemediğinden, yalnızca senkronize yöntemler kullanmalıyım.
Dinei

5
@StephenCleary 1) GeneXus 3. pt aracı ve kaynak koduna erişimim yok; 2) GeneXus'un "işlevler" uygulamaları bile yoktur, bu yüzden bu tür şeylerle nasıl bir "geri çağırma" uygulayabileceğimin farkında olamıyorum. Şüphesiz, Taskeşzamanlı olarak kullanmaktan daha zor bir çözüm olacaktır ; 3) GeneXus'u , sadece eşzamansız olarak bazı yöntemleri ortaya koyan MongoDB C # sürücüsü ile entegre ediyorum
Dinei

1
@ ygoe: gibi zaman uyumsuz bir kilit kullanın SemaphoreSlim.
Stephen Cleary

Yanıtlar:


456

İşte tüm durumlar için (askıya alınmış dağıtım programları dahil) işe yaradığını bulduğum bir geçici çözüm. Bu benim kodum değil ve hala tam olarak anlamak için çalışıyorum, ama işe yarıyor.

Kullanarak çağrılabilir:

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

Kod buradan

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

28
Bunun nasıl çalıştığı hakkında bir arka plan için, Stephen Toub (Bay Parallel) bu konuda bir dizi yazı yazdı. Bölüm 1 Bölüm 2 Bölüm 3
Cameron MacFarland

18
John'un kodunu lambdas'daki görevleri sarmadan çalışacak şekilde güncelledim: github.com/tejacques/AsyncBridge . Aslında async blokları ile using deyimini kullanırsınız. Bir kullanım bloğunun içindeki her şey, sonunda bir bekleme ile eşzamansız olarak gerçekleşir. Dezavantajı, görevi bir geri aramada kendiniz açmanız gerektiğidir, ancak özellikle de aynı anda birkaç asenkron işlevi çağırmanız gerekiyorsa, hala oldukça zariftir.
Tom Jacques

17
@StephenCleary Genellikle bazen tek bir olanaksız durumda kendinizi bulmak, kod tüm yol aşağı zaman uyumsuz olması gerektiğini size katılıyorum rağmen sahip bir senkron çağrısı olarak zorlamak. Temel olarak, benim durumum tüm veri erişim kodumun zaman uyumsuz olduğunu. Site haritasına dayalı bir site haritası oluşturmam gerekiyordu ve kullandığım üçüncü taraf kütüphanesi MvcSitemap. Şimdi kişi bunu DynamicNodeProviderBasetemel sınıf aracılığıyla genişletirken , bunu bir asyncyöntem olarak bildiremez . Ya yeni bir kütüphane ile değiştirmek zorunda kaldım, ya da sadece eşzamanlı op çağırdım.
justin.lovell

6
@ justin.lovell: Evet, kütüphane sınırlamaları bizi en azından kütüphane güncellenene kadar saldırıya zorlayabilir. MvcSitemap bir kesmek gerekli olduğu bir durum gibi görünüyor (MVC filtreleri ve alt eylemleri de); İnsanları genel olarak bundan caydırıyorum, çünkü bu tür hackler gerekli olmadığında çok sık kullanılıyor . Özellikle MVC ile, bazı ASP.NET/MVC API'leri bir sahip olduklarını varsayar AspNetSynchronizationContext, bu yüzden bu API'leri çağırıyorsanız bu özel kesmek işe yaramaz.
Stephen Cleary

5
Bu kod çalışmaz. Bir havuz iş parçacığından çağrılırsa iş parçacığı açlık kilitlenmesini tetikleyebilir. Arayan, işlemin tamamlanmasını beklemeyi engeller, bu da iş parçacığı havuzunu tükettiğinde asla gerçekleşmeyebilir. Bkz bu yazıyı .
ZunTzu

318

Haberiniz olsun bu cevabı üç yaşındadır. Ben çoğunlukla .Net 4.0 ile bir deneyime dayanarak yazdım ve özellikle 4.5 ile çok az async-await. Genel olarak konuşmak gerekirse, bu basit ve basit bir çözümdür, ancak bazen işleri bozar. Lütfen yorumlardaki tartışmayı okuyun.

Net 4.5

Sadece şunu kullanın:

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

Bakınız: TaskAwaiter , Task.Result , Task.RunSynchronously


Net 4.0

Bunu kullan:

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

x.AsyncWaitHandle.WaitOne();

...veya bu:

task.Start();
task.Wait();

67
.Resultbazı senaryolarda bir kilitlenme üretebilir
Jordy Langen

122
Resultolabilir kolayca kilitlenmeye neden asynckodu blogumda tarif olarak,.
Stephen Cleary

8
@StephenCleary Yayınınızı okudum ve kendim denedim. Dürüst olmak gerekirse microsoft birisinin gerçekten sarhoş olduğunu düşünüyorum ...
Winforms

9
Soru, zaman uyumsuz yöntemle döndürülen bir Görev ile ilgilidir. Bu tür bir Görev zaten başlatılmış, yürütülmüş veya iptal edilmiş olabilir, bu nedenle Task.RunSynchronously yönteminin kullanılması InvalidOperationException ile sonuçlanabilir . MSDN sayfasına bakın: Task.RunSynchronously Yöntemi . Ayrıca, bu Görev muhtemelen Task.Factory.StartNew veya Task.Run yöntemleri (async yöntemi içinde) tarafından oluşturulur , bu yüzden yeniden başlatmayı denemek tehlikelidir. Bazı yarış koşulları çalışma zamanında meydana gelebilir. Diğer elinde, Task.Wait ve Task.Result sonuçta kilitlenmeye neden olabilir.
sgnsajgon

4
Çalıştır Eşzamanlı olarak benim için çalıştı ... Bir şey eksik olup olmadığımı bilmiyorum ama bu işaretli cevabın dehşetini tercih ediyor - sadece orada durmak için kod test etmek için zaman uyumsuzluğu kapatmanın bir yolunu arıyordum asılı UI
JonnyRaa

121

Kimse bundan bahsetmedi şaşırttı:

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

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

Buradaki diğer yöntemlerden bazıları kadar hoş değil, ancak aşağıdaki faydaları var:

  • istisnaları yutmaz (gibi Wait)
  • AggregateException(gibi Result) içine atılan istisnaları sarmaz
  • her ikisi için de çalışır Taskve Task<T>( kendiniz deneyin! )

Ayrıca, GetAwaiterördek türünde olduğundan, bu , yalnızca Görevler için değil, zaman uyumsuz bir yöntemden ( ConfiguredAwaitableveya gibi YieldAwaitable) döndürülen herhangi bir nesne için çalışmalıdır .


edit: Bu yaklaşımın (ya da kullanımın .Result) .ConfigureAwait(false)her beklediğinizde eklediğinizden emin olmadıkça BlahAsync()( muhtemelen yalnızca doğrudan çağırdığı değil) ulaşılabilecek tüm zaman uyumsuz yöntemler için mümkün olduğunu unutmayın. Açıklama .

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

Her yere eklemek için çok tembelseniz .ConfigureAwait(false)ve performansı umursamıyorsanız, alternatif olarak

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()

1
Basit şeyler için benim için çalışıyor. Ayrıca, yöntem bir IAsyncOperation döndürürse, önce bir göreve dönüştürmek zorunda kaldı: BlahAsync (). AsTask (). GetAwaiter (). GetResult ();
Lee McPherson

3
Bu, bir asmx web yöntemi içinde bir kilitlenmeye neden oldu. Bununla birlikte, yöntem çağrısını bir Task.Run () öğesine kaydırmak işe yaradı: Task.Run (() => BlahAsync ()). GetAwaiter (). GetResult ()
Augusto Barreto

Bu yaklaşımı en iyi sözdiziminden hoşlanıyorum çünkü lambda içermiyor.
dythim

25
Kendi bağlantınızı eklemek için lütfen diğer kişilerin yanıtlarını DÜZENLEMEYİN. Cevabınızın daha iyi olduğuna inanıyorsanız, bunun yerine yorum olarak bırakın.
Rachel

1
docs.microsoft.com/en-us/dotnet/api/…GetAwaiter() , "Bu yöntem doğrudan kodda kullanmak yerine derleyici kullanıcılarına yöneliktir" der .
Theophilus

75

Görevi iş parçacığı havuzunda çalıştırmak, zamanlayıcıyı eşzamanlı olarak çalıştıracak şekilde kandırmaya çalışmaktan çok daha kolaydır. Bu şekilde kilitlenmeyeceğinden emin olabilirsiniz. Bağlam anahtarı nedeniyle performans etkilenir.

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; 

3
Sonra task.Wait () öğesini çağırırsınız. Veri türü basitçe Görev'dir.
Michael L Perry

1
Diyelim ki DoSomethingAsync () bütünüyle uzun süredir çalışan bir async yöntemi (dahili olarak uzun süren bir görev bekliyor), ancak hızlı bir şekilde arayana bir akış kontrolü verir, böylece lambda argüman çalışması da hızlı bir şekilde sona erer. Tusk.Run () sonucu Görev <Görev> veya Görev <Görev <>> olabilir , bu nedenle hızlı bir şekilde tamamlanan bir dış görev sonucu bekliyorsunuz, ancak iç görev (zaman uyumsuz yöntemde uzun süredir devam eden işi beklemeniz nedeniyle) hala çalışıyor. Sonuçlar, async yönteminin eşzamanlı davranışını elde etmek için muhtemelen Unwrap () yaklaşımını (@ J.Lennon post'ta yapıldığı gibi ) kullanmamız gerektiğidir .
sgnsajgon

5
@sgnsajgon Yanılıyorsunuz. Task.Run, Task.Factory.StartNew öğesinden farklıdır, çünkü sonucu otomatik olarak zaten açar. Bkz bu yazıyı .
ZunTzu

1
Task.Run(DoSomethingAsync)Bunun yerine yazabilir miyim ? Bu, bir seviye temsilciyi kaldırır.
ygoe

1
Evet. Bununla birlikte, ters yöne gitmek Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());daha açıktır ve @sgnsajgon tarafından bir Görev <Görev <MyResult>> Görevi döndürüyor olabileceği endişesini giderir. Task.Run dosyasının aşırı yüklenmesi her iki şekilde de seçilir, ancak zaman uyumsuz delege niyetinizi açıkça gösterir.
Michael L Perry

57

Async / await hakkında öğreniyorum ve eşzamanlı olarak bir async yöntemini çağırmak gereken bir durumla karşılaştım. Bunu nasıl yapabilirim?

En iyi cevap, ayrıntıların "durumun" ne olduğuna bağlı olarak , yapmamanızdır .

Bir mülk alıcısı / belirleyicisi mi? Çoğu durumda, "eşzamansız özellikler" den daha asenkron yöntemlere sahip olmak daha iyidir. (Daha fazla bilgi için eşzamansız mülkler hakkındaki blog gönderime bakın ).

Bu bir MVVM uygulaması ve asenkron veri bağlama yapmak istiyor musunuz? Ardından , eşzamansız veri bağlama hakkındaki MSDN makalemdeNotifyTask açıklandığı gibi, benim gibi bir şey kullanın .

Bir kurucu mu? O zaman muhtemelen asenkron bir fabrika yöntemini düşünmek istersiniz. (Daha fazla bilgi için, zaman uyumsuz kurucular hakkındaki blog yayınıma bakın ).

Zaman uyumsuz senkronizasyon yapmaktan her zaman daha iyi bir yanıt vardır.

Durumunuz için mümkün değilse (ve burada durumu açıklayan bir soru sorarak bunu biliyorsanız ), o zaman sadece senkron kod kullanmanızı tavsiye ederim. Async en iyisi; tüm yollarla senkronizasyon en iyisidir. Eşzamansız senkronizasyon önerilmez.

Ancak, zaman uyumsuz senkronizasyonun gerekli olduğu birkaç durum vardır. Özellikle, kullanıcılara böylece çağıran kod tarafından kısıtlanır sahip senkronizasyonu olmak (ve kesinlikle hiçbir yolu yoktur yeniden düşünmek veya yeniden yapı asenkroniye izin verecek şekilde kodu), ve sen sahip koduna zaman uyumsuz aramak için. Bu çok nadir bir durumdur, ancak zaman zaman ortaya çıkar.

Bu durumda, brownfield asyncgeliştirme hakkındaki makalemde açıklanan kesmeklerden birini kullanmanız gerekir , özellikle:

  • Engelleme (ör GetAwaiter().GetResult().). Bunun kilitlenmelere neden olabileceğini unutmayın ( blogumda açıkladığım gibi).
  • Kodu bir iş parçacığı havuzu iş parçacığında (örn Task.Run(..).GetAwaiter().GetResult().) Çalıştırma . Bunun yalnızca eşzamansız kod bir iş parçacığı havuzu iş parçacığında çalıştırılabilirse (yani, bir UI veya ASP.NET içeriğine bağımlı değilse) işe yarayacağını unutmayın.
  • İç içe ileti döngüleri. Bunun yalnızca eşzamansız kodun yalnızca tek bir iş parçacığı bağlamını varsayması durumunda işe yarayacağını, belirli bir bağlam türünün (birçok UI ve ASP.NET kodu belirli bir bağlam beklediğini) işe yarayacağını unutmayın .

Yuvalanmış mesaj döngüleri tüm saldırıların en tehlikelidir, çünkü yeniden girişe neden olur . Yeniden giriş konusunda akıl yürütmek son derece zordur ve (IMO) Windows'taki uygulama hatalarının çoğunun nedenidir. Eğer UI iş parçacığı üzerinde konum ve bir çalışma sırasına engellediyseniz Özellikle, (tamamlanmasını zaman uyumsuz iş için bekleyen), sonra CLR aslında sizin için bazı mesaj pompalama yapar - bu aslında bazı Win32 iletileri işlemek edeceğiz içinden senin kod . Oh, ve hangi mesajların hiçbir fikriniz yok - Chris Brumme "Neyin pompalanacağını tam olarak bilmek iyi olmaz mıydı?" Ne yazık ki, pompalama ölümcül kavrayışın ötesinde bir siyah sanattır. , o zaman gerçekten bilme umudumuz yok.

Yani, bir UI iş parçacığında böyle engellediğinizde, sorun istiyorsunuz. Aynı makaleden başka bir cbrumme alıntı: "Zaman zaman, şirket içindeki veya dışındaki müşteriler bir STA [UI iş parçacığı] üzerinde yönetilen engelleme sırasında mesaj pompalama olduğunu keşfeder. Bu meşru bir endişe, çünkü çok zor olduğunu biliyorlar yeniden giriş karşısında sağlam bir kod yazmak. "

Evet öyle. Yeniden giriş karşısında sağlam kod yazmak çok zor. Ve iç içe ileti döngüleri , yeniden giriş karşısında sağlam bir kod yazmaya zorlar . Bu nedenle bu soru için kabul (ve en çok upvoted) cevap ise son derece tehlikeli pratikte.

Diğer tüm seçeneklerin tamamen dışındaysanız - kodunuzu yeniden tasarlayamazsanız, senkronize olmayacak şekilde yeniden yapılandıramazsınız - değiştirilemeyen arama kodu senkronize olmaya zorlanırsınız - aşağı akış kodunu senkronize olacak şekilde değiştiremezsiniz - engelleyemezsiniz - async kodunu ayrı bir iş parçacığında çalıştıramazsınız - o zaman ve ancak o zaman reentrancy'yi kucaklamayı düşünmelisiniz.

Kendinizi bu köşede bulursanız, Dispatcher.PushFrameWPF uygulamaları , Application.DoEventsWinForm uygulamaları için döngü ve genel durum için kendim gibi bir şey kullanmanızı öneririm AsyncContext.Run.


Stephen, harika bir cevap verdiğiniz çok benzer bir soru var. Bunlardan birinin yinelenen veya belki birleştirme isteği olarak kapatılabileceğini veya önce meta olarak getirilebileceğini düşünüyor musunuz (her q ~ 200K'da 200'den fazla oy alıyor)? Öneriler?
Alexei Levenkov

1
@AlexeiLevenkov: Bunu birkaç nedenden ötürü doğru yapmak istemiyorum: 1) Bağlantılı sorunun cevabı oldukça güncel değil. 2) Konuyla ilgili, mevcut herhangi bir SO Q / A'dan daha eksiksiz olduğunu düşündüğüm bir makale yazdım . 3) Bu soruya kabul edilen cevap son derece popüler. 4) Kabul edilen bu cevaba şiddetle karşıyım . Yani, bunun bir kopyası olarak kapatmak gücün kötüye kullanılması olacaktır; bunun bir kopyası (veya birleşme) olarak kapatılması tehlikeli bir cevabı daha da güçlendirecektir. Bırakmasına izin veriyorum ve topluluğa bırakıyorum.
Stephen Cleary

Tamam. Bunu bir şekilde meta'ya getirmeyi düşüneceğim.
Alexei Levenkov

9
Bu cevap kafamda çok yol kat ediyor. "Async'i sonuna kadar kullan" açık bir şekilde takip edilememesi nedeniyle kafa karıştırıcıdır. Zaman uyumsuz Main()yöntemi olan bir program derlenmez; Bir noktada ettik got senkronizasyon ve zaman uyumsuz dünyalar arasındaki uçurumu. Bu " çok nadir bir durum" değildir , kelimenin tam anlamıyla asenkron yöntem olarak adlandırılan her programda gereklidir. "Eşzamansız senkronizasyon yap" seçeneği yoktur , yalnızca o anda yazmakta olduğunuz yöntemde onu omuzlamak yerine arama yöntemine yüklenen bir şönt seçeneği vardır.
Mark Amery

1
Harika. asyncŞimdi uygulamamdaki tüm yöntemleri uygulamak üzereyim . Ve bu çok fazla. Bu sadece varsayılan olamaz mı?
ygoe

25

Sorunuzu doğru okuyorsam - bir zaman uyumsuz yönteme eşzamanlı çağrı isteyen kod askıya alınmış bir dağıtım iş parçacığında yürütülüyor. Async yöntemi tamamlanana kadar bu iş parçacığını eşzamanlı olarak engellemek istersiniz .

C # Task5'teki asenkron yöntemler, yöntemin kaputun altındaki parçalara etkili bir şekilde kesilmesi ve tüm çalkalanmanın genel tamamlanmasını takip edebilen bir yöntem döndürülmesi ile güçlendirilir. Ancak, doğranmış yöntemlerin nasıl yürütüldüğü awaitoperatöre iletilen ifadenin türüne bağlı olabilir .

Çoğu zaman, awaittür ifadesi kullanırsınız Task. Görevin awaitkalıbın uygulanması “akıllı” dır, zira SynchronizationContextbu temelde aşağıdakilerin gerçekleşmesine neden olur:

  1. Giren iş parçacığı awaitbir Dağıtıcı veya WinForms ileti döngüsü iş parçacığındaysa, zaman uyumsuzluğu yönteminin parçalarının ileti kuyruğunun işlenmesinin bir parçası olarak gerçekleşmesini sağlar.
  2. Giren awaitiş parçacığı bir iş parçacığı havuzu iş parçacığındaysa, zaman uyumsuz yöntemin kalan parçaları iş parçacığı havuzunda herhangi bir yerde oluşur.

Bu yüzden muhtemelen sorunla karşılaşıyorsunuz - async yöntemi uygulaması askıya alınmış olsa bile geri kalanını Dispatcher'da çalıştırmaya çalışıyor.

.... yedekleme! ....

Soruyu sormam gerekiyor, neden zaman uyumsuz bir yöntemle eşzamanlı olarak engellemeye çalışıyorsunuz? Bunu yapmak, yöntemin neden eşzamansız olarak adlandırılmasını istediği amacını bozacaktır. Genel olarak, awaitbir Dispatcher veya UI yönteminde kullanmaya başladığınızda , tüm UI akış zaman uyumsuzluğunu değiştirmek istersiniz. Örneğin, çağrı kaydınız aşağıdaki gibi bir şeyse:

  1. [Üst] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()- WPFveya WinFormsKod
  6. [Mesaj Döngüsü] - WPFveya WinFormsMesaj Döngüsü

Daha sonra kod zaman uyumsuz olarak kullanılmak üzere dönüştürüldüğünde, genellikle

  1. [Üst] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()- WPFveya WinFormsKod
  6. [Mesaj Döngüsü] - WPFveya WinFormsMesaj Döngüsü

Aslında Yanıtlıyor

Yukarıdaki AsyncHelpers sınıfı aslında çalışır, çünkü iç içe geçmiş bir ileti döngüsü gibi davranır, ancak Dispatcher'ın kendisinde yürütülmeye çalışmak yerine kendi paralel teknisyenini Dispatcher'a yükler. Sorununuz için bir çözüm var.

Başka bir geçici çözüm, zaman uyumsuz yönteminizi bir threadpool iş parçacığında yürütmek ve sonra tamamlanmasını beklemektir. Bunu yapmak kolaydır - bunu aşağıdaki snippet ile yapabilirsiniz:

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

Son API Task.Run (...) olacaktır, ancak CTP ile Ex soneklerine ihtiyacınız olacaktır ( burada açıklama ).


Ayrıntılı açıklama için +1, ancak TaskEx.RunEx(GetCustomers).Resultaskıya alınmış bir dağıtım iş parçacığında çalıştırıldığında uygulama askıda kalıyor. Ayrıca, GetCustomers () yöntemi normalde zaman uyumsuz çalışır, ancak bir durumda senkronize olarak çalışması gerekir, bu yüzden yöntemin bir senkronizasyon sürümü oluşturmadan bunu yapmak için bir yol arıyordu.
Rachel

+1 için "neden zaman uyumsuz bir yöntemle eşzamanlı olarak engellemeye çalışıyorsunuz?" asyncYöntemleri doğru kullanmanın her zaman bir yolu vardır ; yuvalanmış döngülerden kesinlikle kaçınılmalıdır.
Stephen Cleary

24

Bu benim için iyi çalışıyor

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

Ayrıca kullanmak gerekir Task.Unwrap sizin, çünkü yöntem Task.Wait beyanı (yarattığı dış Görev bekleyen neden olur Task.Run değil iç için,) bekliyoruz t Görev uzatma yönteminin parametresi olarak geçti. Kişisel Task.Run metot bize değil Görev <T>, ancak Görev <Görev <T >>. Bazı basit senaryolarda çözüm kullanarak örneğin nedeniyle TaskScheduler optimizasyon çalışmaları, olabilir TryExecuteTaskInline yöntemi sırasında mevcut parçacık kapsamındaki görevleri yürütmek için bekleyin benim yorumum de operasyon zarar bulursanız bakmak bu cevap.
sgnsajgon

1
Bu doğru değil. Görev.Çalışma Görev <T> döndürecektir. Bu aşırı yüke bakın msdn.microsoft.com/en-us/library/hh194918(v=vs.110).aspx
Clement

Bunun nasıl kullanılması gerekiyor? MyAsyncMethod().RunTaskSynchronously();
WPF'deki

18

Görev senkronize ve UI iş parçacığını engellemeden çalıştırmak için bulduk en basit yolu RunSynchronously () gibi kullanmaktır:

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

Benim durumumda, bir şey olduğunda tetikleyen bir olay var. Kaç kere olacağını bilmiyorum. Yani, olayımda yukarıdaki kodu kullanıyorum, bu yüzden her tetiklendiğinde bir görev oluşturur. Görevler eşzamanlı olarak yürütülür ve benim için harika çalışır. Ne kadar basit olduğunu düşünerek bunun öğrenilmesinin çok uzun sürdüğüne şaşırdım. Genellikle öneriler çok daha karmaşık ve hataya açıktır. Bu basit ve temiz oldu.


1
Ancak, zaman uyumsuz kod ihtiyacımız olan bir şey döndürdüğünde bu yöntemi nasıl kullanabiliriz?
S.Serpooshan

16

Birkaç kez, çoğunlukla birim testlerinde veya bir Windows hizmet geliştirmesinde karşılaştım. Şu anda her zaman bu özelliği kullanıyorum:

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

Çok basit, kolay ve hiç problem yaşamadım.


Benim için kilitlenmeyen tek kişi bu.
AndreFeijo

15

Bu kodu Microsoft.AspNet.Identity.Core bileşeninde buldum ve çalışıyor.

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

Sadece küçük bir not - bu yaklaşım:

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

WinRT için çalışıyor.

Açıklamama izin ver:

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

Üstelik bu yaklaşım yalnızca Windows Mağazası çözümleri için geçerlidir!

Not: Yönteminizi diğer zaman uyumsuz yöntem içinde çağırırsanız, bu yol iş parçacığı için güvenli değildir (@Servy yorumlarına göre)


Bu çözümü açıkladım, EDIT bölümünü kontrol edin.
RredCat

2
Bu, eşzamansız durumlarda çağrıldığında kolayca kilitlenmelere neden olabilir.
13'te

@Servy mantıklı. Yani Wait (timeOut) kullanarak doğru olarak almak yardımcı olabilir, değil mi?
RredCat

1
Daha sonra, işlem gerçekten tamamlanmadığında zaman aşımına ulaşılması konusunda endişelenmeniz gerekir, bu çok kötüdür ve aynı zamanda kilitlenmenin olduğu durumlarda zaman aşımına kadar beklemek için harcanan zaman (ve bu durumda hala devam ediyorsunuz) bitmediğinde). Yani hayır, bu sorunu çözmez.
13'te

@Servy Çözümüm için uygulamak zorunda olduğum anlaşılıyor CancellationToken.
RredCat

10

Kodunuzda, görevin yürütülmesi için ilk bekleyişiniz , ancak bu süreyi başlatmadığınız için süresiz olarak bekler. Bunu dene:

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

Düzenle:

İstisna olduğunu söylüyorsun. Lütfen yığın izleme dahil daha fazla ayrıntı yayınlayın.
Mono aşağıdaki test durumunu içerir :

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

        Assert.AreEqual (1, val);
}

Bunun sizin için işe yarayıp yaramadığını kontrol edin. Değilse de, pek olası olmasa da, Async CTP'nin garip bir yapısına sahip olabilirsiniz. Eğer işe yararsa, derleyicinin tam olarak ne ürettiğini ve Taskörneklemenin bu örnekten nasıl farklı olduğunu incelemek isteyebilirsiniz .

Düzenleme 2:

Ben ne zaman açıklanan istisna oluşur Reflektör kontrol m_actionolduğunu null. Bu biraz garip, ama Async CTP konusunda uzman değilim. Dediğim gibi, kodunuzu derleme ve tam olarak nasıl görmelisiniz Taskonun nasıl oluyor herhangi örneği ediliyor m_actionIS null.


PS Ara sıra downvotes ile anlaşma nedir? Ayrıntılı düşünmek ister misiniz?


Sorumu biraz daha açıklamaya çalıştığım kodu yapmak için ayarladım. RunSynchronously bir hata döndürüyor RunSynchronously may not be called on a task unbound to a delegate. Google bunun için tüm sonuçlar çince olduğu için yardım etmiyor ...
Rachel

Bence fark, Görevi yaratmamam ve sonra çalıştırmayı denemem. Bunun yerine, awaitanahtar kelime kullanıldığında görev zaman uyumsuz yöntem tarafından oluşturulur . Daha önceki yorumumda yayınlanan istisna, Google'da bulamadığım ve bir neden veya çözüm bulamadığım birkaç kişiden biri olmasına rağmen, aldığım istisna.
Rachel

1
asyncve asyncanahtar kelimeler sözdizimi şekerinden başka bir şey değildir. Derleyici oluşturmak için kod üretir Task<Customer>, GetCustomers()böylece ilk baktığım yer burasıdır. İstisna gelince, sadece istisna türü ve yığın izlemesi olmadan işe yaramaz olan istisna mesajı gönderdiniz. İstisna ToString()yöntemini çağırın ve sorudaki çıktıyı alın.
Dan Abramov

@gaearon: Orijinal sorumda istisna ayrıntılarını ve yığın izlemesini yayınladım.
Rachel

2
@gaearon Sanırım yayınınız soru için geçerli olmadığından aşağı oylarınız olduğunu düşünüyorum. Tartışma basit Görev döndürme yöntemleri ile değil asenkron bekle yöntemleri ile ilgilidir. Dahası, bence async-await mekanizması bir sözdizimi şekeri, ama o kadar da önemsiz değil - devam, bağlam yakalama, yerel bağlam sürdürme, gelişmiş yerel istisna işleme ve daha fazlası var. Daha sonra, async yönteminin sonucunda RunSynchronously yöntemini çağırmamalısınız , çünkü tanım gereği asenkron yöntem şu anda en az zamanlanmış olan ve bir kereden fazla çalışan durumda olan Görevi döndürmelidir.
sgnsajgon

9

Net 4.6'da test edilmiştir. Ayrıca kilitlenmeyi de önleyebilir.

Zaman uyumsuz yöntem döndürme için Task.

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

Zaman uyumsuz yöntem döndürme için Task<T>

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

Düzenle :

Arayan, iş parçacığı havuzu iş parçacığında çalışıyorsa (veya arayan da bir görevdeyse), bazı durumlarda kilitlenmeye neden olabilir.


1
Neredeyse 8 yıl sonra cevabım :) İkinci örnek - esasen kullanılan tüm zamanlanmış bağlamda bir kilitlenme üretecektir (konsol uygulaması / .NET çekirdek / masaüstü uygulaması / ...). Burada i hakkında şimdi neden bahsettiğimi daha özeti var: medium.com/rubrikkgroup/...
W92

Resulteşzamanlı bir çağrı istiyorsanız iş için mükemmeldir, aksi halde düpedüz tehlikeli. ResultAdında ya da Resultdüşüncesinde bir engelleme çağrısı olduğunu gösteren hiçbir şey yoktur . Gerçekten yeniden adlandırılmalıdır.
Zodman

5

kod snip'in altında kullanın

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));

4

Neden şöyle bir çağrı oluşturmuyorsunuz?

Service.GetCustomers();

bu zaman uyumsuz değil.


4
Bu çalışmayı alamazsam benim yaptığım şey bu olacak ... Async sürümüne ek olarak bir Sync sürümü oluştur
Rachel

3

Bu yanıt, .NET 4.5 için WPF kullanan herkes için tasarlanmıştır.

Task.Run()GUI iş parçacığında yürütmeye çalışırsanız , işlev tanımınızda anahtar sözcük task.Wait()yoksa süresiz olarak kilitlenir async.

Bu uzantı yöntemi, GUI iş parçacığında olup olmadığımızı kontrol ederek ve öyleyse görevi WPF dağıtıcı iş parçacığında çalıştırarak kontrol ederek sorunu çözer.

Bu sınıf, MVVM özellikleri veya async / await kullanmayan diğer API'lara bağımlılıklar gibi kaçınılmaz olduğu durumlarda asenkron / beklenen dünya ile asenkron olmayan / beklenen dünya arasında tutkal görevi görebilir.

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

3

Basitçe aramak .Result;veya .Wait()yorumlarda söylendiği gibi çıkmazlar için bir risktir. Çoğumuz onelinerleri sevdiğimiz için bunları.Net 4.5<

Zaman uyumsuz bir yöntemle bir değer elde etme:

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

Eşzamanlı olarak bir eşzamansız yöntem çağırıyor

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

Kullanımı nedeniyle kilitlenme sorunu oluşmaz Task.Run.

Kaynak:

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


1

Aşağıdaki yardımcı yöntem de sorunu çözebilir düşünüyorum.

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

Aşağıdaki şekilde kullanılabilir:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);

1
Lütfen oylamayı açıklayın
donttellya

2
... Bu cevabın neden oy kullanıldığını merakla merak ediyorum.
donttellya

Bu gerçek bir "zaman uyumlu" değildir.
tmt

ve her şey bir yana, bu çok kötü bir fikir.
Dan Pantry

1
Hemen hemen aynı kodu (satır satır aynı) yazdım, bunun yerine otomatik sıfırlama olayı yerine SemaphoreSlim'i kullandım. Keşke bunu daha önce görmüş olsaydım. Bu yaklaşımı kilitlenmeleri önlemek ve eşzamansız kodunuzun gerçek eşzamansız senaryolarda olduğu gibi çalışmasını sağlamak için buluyorum. Bunun neden kötü bir fikir olduğundan emin değilim. Yukarıda gördüğüm diğer yaklaşımlardan çok daha temiz görünüyor.
tmrog

0

Bu benim için çalışıyor

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

-1

SpinWait'in bunun için oldukça iyi çalıştığını gördüm.

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;

Yukarıdaki yaklaşımın .Result veya .Wait () kullanmasına gerek yoktur. Ayrıca, görevin hiçbir zaman tamamlanmaması durumunda sonsuza kadar takılı kalmamanız için bir zaman aşımı belirtmenize izin verir.


1
Aşağı oy, birisinin bu yöntemi sevmediğini gösteriyor. Bunun olumsuz tarafı hakkında yorum yapabilen biri var mı?
Grax32

Downvoter'ın neden downvote verildiğini söylememesi durumunda, birisi onu vurabilir mi? :-)
Curtis

1
Bu oylama (döndürme), delege saniyede 1000 defaya kadar havuzdan iplik alacak. Görev bittikten hemen sonra kontrolü geri vermeyebilir ( 10 + ms'ye kadar hata). Zaman aşımı ile bitirilirse görev çalışmaya devam eder, bu da zaman aşımını pratik olarak işe yaramaz hale getirir.
Sinatr

Aslında, bu kodumu her yerde kullanıyorum ve koşul karşılandığında, SpinWaitSpinUntil () hemen çıkar. Yani hangisi önce gelirse, 'koşul karşılandı' veya zaman aşımı, görevden çıkar. Koşmaya devam etmiyor.
Curtis

-3

Wp8'de:

Sar:

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

Bunu aramak:

GetCustomersSynchronously();

3
Hayır, bu işe yaramayacak, çünkü görev yapıcıdan delege beklemiyor (bir delege ve bir görev değil ..)
Rico Suter

-4
    private int GetSync()
    {
        try
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            int result = null;

            Parallel.Invoke(async () =>
            {
                result = await SomeCalcAsync(5+5);
                mre.Set();
            });

            mre.WaitOne();
            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }

-5

Veya şununla gidebilirsiniz:

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

Bunun derlenmesi için uzantı derlemesine başvurduğunuzdan emin olun:

System.Net.Http.Formatting

-9

Benim için işe yarayan aşağıdaki kodu deneyin:

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}
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.