Bagaimana saya menjalankan metode Tugas asinkron <T> secara sinkron?

628

Saya belajar tentang async / menunggu, dan mengalami situasi di mana saya perlu memanggil metode async secara serempak. Bagaimana saya bisa melakukan itu?

Metode Async:

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

Penggunaan normal:

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

Saya sudah mencoba menggunakan yang berikut ini:

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

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

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

Saya juga mencoba saran dari sini , tetapi tidak berfungsi ketika operator dalam keadaan ditangguhkan.

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

Berikut adalah pengecualian dan susun jejak dari panggilan RunSynchronously:

System.InvalidOperationException

Pesan : RunSynchronously tidak dapat dipanggil pada tugas yang tidak terikat ke delegasi.

InnerException : null

Sumber : 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()
Rachel
sumber
46
Jawaban terbaik untuk pertanyaan "Bagaimana saya bisa memanggil metode async secara sinkron" adalah "jangan". Ada peretasan untuk mencoba memaksanya bekerja, tetapi mereka semua memiliki perangkap yang sangat halus. Sebagai gantinya, buat cadangan dan perbaiki kode yang membuat Anda "perlu" melakukan ini.
Stephen Cleary
57
@Stephen Cleary Sepenuhnya setuju, tetapi kadang-kadang itu tidak dapat dihindari, seperti ketika kode Anda bergantung pada beberapa API pihak ke-3 yang tidak menggunakan async / menunggu. Selain itu, jika mengikat ke properti WPF saat menggunakan MVVM, secara harfiah tidak mungkin untuk menggunakan async / menunggu karena ini tidak didukung pada properti.
Contango
3
@StephenCleary Tidak selalu. Saya sedang membangun DLL yang akan diimpor di GeneXus . Itu tidak mendukung kata kunci async / menunggu, jadi saya harus hanya menggunakan metode sinkron.
Dinei
5
@StephenCleary 1) GeneXus adalah alat pt ke-3 dan saya tidak memiliki akses ke kode sumbernya; 2) GeneXus bahkan tidak memiliki implementasi "fungsi", jadi saya tidak bisa menyadari bagaimana saya bisa menerapkan "panggilan balik" dengan jenis hal ini. Tentunya itu akan menjadi solusi yang lebih sulit daripada menggunakan secara Taskserempak; 3) Saya mengintegrasikan GeneXus dengan driver MongoDB C # , yang mengekspos beberapa metode hanya secara tidak sinkron
Dinei
1
@ygoe: Gunakan kunci yang kompatibel dengan async, seperti SemaphoreSlim.
Stephen Cleary

Jawaban:

456

Berikut adalah solusi yang saya temukan yang berfungsi untuk semua kasus (termasuk dispatcher yang ditangguhkan). Ini bukan kode saya dan saya masih bekerja untuk sepenuhnya memahaminya, tetapi itu berhasil.

Itu bisa disebut menggunakan:

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

Kode berasal dari sini

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;
        }
    }
}
Rachel
sumber
28
Untuk beberapa latar belakang tentang cara kerjanya, Stephen Toub (Mr Parallel) menulis serangkaian posting tentang ini. Bagian 1 Bagian 2 Bagian 3
Cameron MacFarland
18
Saya memperbarui kode John untuk bekerja tanpa membungkus tugas dalam lambdas: github.com/tejacques/AsyncBridge . Pada dasarnya Anda bekerja dengan blok async dengan pernyataan menggunakan. Apa pun di dalam blok menggunakan terjadi secara tidak sinkron, dengan menunggu di akhir. Kelemahannya adalah Anda harus membuka sendiri tugasnya dalam panggilan balik, tetapi masih cukup elegan, terutama jika Anda perlu memanggil beberapa fungsi asink sekaligus.
Tom Jacques
17
@StephenCleary Meskipun saya umumnya setuju dengan Anda bahwa kode harus async sepenuhnya, kadang-kadang Anda menemukan diri Anda dalam situasi yang tidak layak di mana orang harus memaksanya sebagai panggilan sinkron. Pada dasarnya, situasi saya adalah semua kode akses data saya dalam mode async. Saya perlu membangun peta situs berdasarkan sitemap dan perpustakaan pihak ketiga yang saya gunakan adalah MvcSitemap. Sekarang ketika seseorang memperluasnya melalui DynamicNodeProviderBasekelas dasar, seseorang tidak dapat mendeklarasikannya sebagai asyncmetode. Entah saya harus mengganti dengan perpustakaan baru, atau hanya memanggil op sinkron.
justin.lovell
6
@ justin.lovell: Ya, batasan pustaka dapat memaksa kami untuk meretas , setidaknya sampai pustaka diperbarui. Kedengarannya seperti MvcSitemap adalah salah satu situasi di mana hack diperlukan (filter MVC dan tindakan anak juga); Saya hanya mencegah orang dari ini secara umum karena peretasan seperti ini terlalu sering digunakan ketika mereka tidak diperlukan. Dengan MVC pada khususnya, beberapa API ASP.NET/MVC menganggap bahwa mereka memiliki AspNetSynchronizationContext, jadi peretasan khusus ini tidak akan berfungsi jika Anda memanggil API tersebut.
Stephen Cleary
5
Kode ini tidak akan berfungsi. Jika dipanggil dari utas pool, ini dapat memicu kebuntuan thread-kelaparan. Penelepon Anda akan memblokir menunggu operasi selesai, yang mungkin tidak pernah terjadi jika ia telah kehabisan utas thread. Lihat artikel ini .
ZunTzu
318

Maklum jawaban ini berumur tiga tahun. Saya menulisnya sebagian besar didasarkan pada pengalaman dengan. Net 4.0, dan sangat sedikit dengan 4,5 terutama dengan async-await. Secara umum itu adalah solusi sederhana yang bagus, tetapi kadang-kadang merusak banyak hal. Silakan baca diskusi di komentar.

.Net 4.5

Gunakan ini saja:

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

Lihat: TaskAwaiter , Task.Result , Task.RunSynchronously


.Net 4.0

Gunakan ini:

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

x.AsyncWaitHandle.WaitOne();

...atau ini:

task.Start();
task.Wait();
AK_
sumber
67
.Resultdapat menghasilkan jalan buntu dalam skenario tertentu
Jordy Langen
122
Resultdapat dengan mudah menyebabkan kebuntuan dalam asynckode , seperti yang saya jelaskan di blog saya.
Stephen Cleary
8
@StephenCleary Saya membaca posting Anda, dan mencobanya sendiri. Jujur saya pikir seseorang di microsoft benar-benar mabuk ... Ini masalah yang sama seperti winforms dan utas latar belakang ....
AK_
9
Pertanyaannya menyangkut Tugas yang dikembalikan dengan metode async. Jenis Tugas semacam itu mungkin sudah dimulai, dijalankan, atau dibatalkan, jadi penggunaan metode Task.RunSynchronous dapat mengakibatkan InvalidOperationException . Lihat halaman MSDN: Metode Task.RunSynchronous . Selain itu, yang Task mungkin dibuat oleh Task.Factory.StartNew atau Task.Run metode (metode async dalam), sehingga berbahaya untuk mencoba memulai lagi. Beberapa kondisi lomba dapat terjadi saat runtime. Di sisi lain, Task.Wait dan Task.Result dapat menyebabkan saya menemui jalan buntu.
sgnsajgon
4
Jalankan bekerja secara sinkron untuk saya ... Saya tidak tahu apakah saya kehilangan sesuatu tetapi ini tampaknya lebih baik daripada kengerian dari jawaban yang ditandai - Saya hanya mencari cara untuk mematikan async untuk menguji kode yang hanya ada untuk berhenti ui dari menggantung
JonnyRaa
121

Terkejut tidak ada yang menyebutkan ini:

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

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

Tidak secantik beberapa metode lain di sini, tetapi memiliki manfaat sebagai berikut:

  • itu tidak menelan pengecualian (seperti Wait)
  • itu tidak akan membungkus pengecualian apa pun yang dilemparkan ke dalam AggregateException(seperti Result)
  • bekerja untuk keduanya Taskdan Task<T>( coba sendiri! )

Juga, karena GetAwaiterdiketik bebek, ini harus bekerja untuk objek apa pun yang dikembalikan dari metode async (suka ConfiguredAwaitableatau YieldAwaitable), bukan hanya Tugas.


sunting: Harap perhatikan bahwa pendekatan ini (atau menggunakan .Result) mungkin menemui jalan buntu, kecuali jika Anda memastikan untuk menambahkan .ConfigureAwait(false)setiap kali Anda menunggu, untuk semua metode async yang mungkin dapat dicapai dari BlahAsync()(bukan hanya yang dipanggil langsung). Penjelasan .

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

Jika Anda terlalu malas untuk menambahkan di .ConfigureAwait(false)mana-mana, dan Anda tidak peduli dengan kinerja, Anda juga dapat melakukannya

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
James Ko
sumber
1
Bekerja untuk saya untuk hal-hal sederhana. Juga, jika metode mengembalikan IAsyncOperation, saya harus mengonversinya menjadi Tugas pertama: BlahAsync (). AsTask (). GetAwaiter (). GetResult ();
Lee McPherson
3
Ini menyebabkan kebuntuan di dalam metode web asmx. Namun demikian, membungkus pemanggilan metode dalam Task.Run () membuatnya berfungsi: Task.Run (() => BlahAsync ()). GetAwaiter (). GetResult ()
Augusto Barreto
Saya suka pendekatan ini paling baik secara sintaksis karena tidak melibatkan lambdas.
dythim
25
Tolong JANGAN edit jawaban orang lain untuk memasukkan tautan ke milik Anda. Jika Anda yakin jawaban Anda lebih baik, tinggalkan itu sebagai komentar.
Rachel
1
docs.microsoft.com/en-us/dotnet/api/… mengatakan tentang GetAwaiter(), "Metode ini ditujukan untuk pengguna kompiler daripada digunakan langsung dalam kode."
Theophilus
75

Jauh lebih mudah untuk menjalankan tugas di kumpulan utas, daripada mencoba menipu penjadwal untuk menjalankannya secara serempak. Dengan begitu Anda dapat yakin bahwa itu tidak akan menemui jalan buntu. Kinerja terpengaruh karena saklar konteks.

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; 
Michael L Perry
sumber
3
Kemudian Anda memanggil task.Wait (). Tipe data hanyalah Tugas.
Michael L Perry
1
Mari kita asumsikan bahwa DoSomethingAsync () adalah metode async yang berjalan lama secara keseluruhan (secara internal ia menunggu tugas yang berjalan lama), tetapi ia mengembalikan kontrol aliran ke pemanggilnya dengan cepat, sehingga kerja argumen lambda juga berakhir dengan cepat. Hasil Tusk.Run () dapat Tugas <Task> atau Tugas <Tugas <>> , jadi Anda sedang menunggu hasil dari tugas luar yang diselesaikan dengan cepat, tetapi tugas dalam (karena menunggu pekerjaan jangka panjang dalam metode async) masih berjalan. Kesimpulannya adalah bahwa kita mungkin perlu menggunakan pendekatan Unwrap () (seperti yang dilakukan dalam posting @ J.Lennon) untuk mencapai perilaku sinkron metode async.
sgnsajgon
5
@sgnsajgon Anda salah. Task.Run berbeda dari Task.Factory.StartNew dalam hal itu secara otomatis membuka hasil. Lihat artikel ini .
ZunTzu
1
Bisakah saya menulis Task.Run(DoSomethingAsync)saja? Ini menghapus satu tingkat delegasi.
ygoe
1
Ya. Pergi ke arah yang berlawanan, karena, seperti dalam Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());lebih eksplisit dan mengatasi kekhawatiran oleh @sgnsajgon bahwa mungkin akan mengembalikan Tugas <Tugas <MyResult>>. Overload Task.Run yang benar dipilih dengan cara apa pun, tetapi delegasi async menjadikan maksud Anda jelas.
Michael L Perry
57

Saya belajar tentang async / menunggu, dan mengalami situasi di mana saya perlu memanggil metode async secara serempak. Bagaimana saya bisa melakukan itu?

Jawaban terbaik adalah Anda tidak , dengan detail tergantung pada apa "situasi" itu.

Apakah ini pengambil properti? Dalam kebanyakan kasus, lebih baik memiliki metode asinkron daripada "properti asinkron". (Untuk info lebih lanjut, lihat posting blog saya di properti asinkron ).

Apakah ini aplikasi MVVM dan Anda ingin melakukan pengikatan data asinkron? Kemudian gunakan sesuatu seperti saya NotifyTask, seperti yang dijelaskan dalam artikel MSDN saya tentang pengikatan data asinkron .

Apakah itu konstruktor? Maka Anda mungkin ingin mempertimbangkan metode pabrik asinkron. (Untuk info lebih lanjut, lihat posting blog saya di konstruktor asinkron ).

Hampir selalu ada jawaban yang lebih baik daripada melakukan sync-over-async.

Jika tidak mungkin untuk situasi Anda (dan Anda tahu ini dengan mengajukan pertanyaan di sini menggambarkan situasi ), maka saya akan merekomendasikan hanya menggunakan kode sinkron. Async adalah yang terbaik; sinkronisasi semua cara adalah yang terbaik kedua. Sinkronisasi-over-async tidak dianjurkan.

Namun, ada beberapa situasi di mana sinkronisasi-over-async diperlukan. Khususnya, Anda dibatasi oleh kode panggilan sehingga Anda harus disinkronkan (dan sama sekali tidak memiliki cara untuk memikirkan kembali atau menyusun kembali kode Anda untuk memungkinkan sinkronisasi), dan Anda harus memanggil kode async. Ini adalah situasi yang sangat langka, tetapi muncul dari waktu ke waktu.

Dalam hal ini, Anda harus menggunakan salah satu peretasan yang dijelaskan dalam artikel saya tentang pengembangan brownfieldasync , khususnya:

  • Pemblokiran (misalnya, GetAwaiter().GetResult()). Perhatikan bahwa ini dapat menyebabkan kebuntuan (seperti yang saya jelaskan di blog saya).
  • Menjalankan kode pada utas kumpulan utas (mis., Task.Run(..).GetAwaiter().GetResult()). Perhatikan bahwa ini hanya akan berfungsi jika kode asinkron dapat dijalankan pada utas kumpulan utas (yaitu, tidak bergantung pada konteks UI atau ASP.NET).
  • Loop pesan bersarang. Perhatikan bahwa ini hanya akan berfungsi jika kode asinkron hanya mengasumsikan konteks single-threaded, bukan tipe konteks spesifik (banyak kode UI dan ASP.NET mengharapkan konteks tertentu).

Loop pesan bersarang adalah yang paling berbahaya dari semua peretasan, karena hal itu menyebabkan pemanggilan kembali . Re-entrancy sangat sulit untuk dipikirkan, dan (IMO) adalah penyebab sebagian besar bug aplikasi pada Windows. Secara khusus, jika Anda berada di utas UI dan Anda memblokir antrian pekerjaan (menunggu pekerjaan async selesai), maka CLR benar-benar melakukan beberapa pemompaan pesan untuk Anda - itu sebenarnya akan menangani beberapa pesan Win32 dari dalam Anda kode . Oh, dan Anda tidak tahu pesan mana - ketika Chris Brumme berkata, "Bukankah lebih baik mengetahui dengan pasti apa yang akan dipompa? Sayangnya, memompa adalah seni hitam yang berada di luar jangkauan pemahaman fana." , maka kita benar-benar tidak memiliki harapan untuk mengetahui.

Jadi, ketika Anda memblokir seperti ini di utas UI, Anda meminta masalah. Kutipan cbrumme lain dari artikel yang sama: "Dari waktu ke waktu, pelanggan di dalam atau di luar perusahaan menemukan bahwa kami memompa pesan selama pemblokiran berhasil pada STA [utas UI]. Ini adalah kekhawatiran yang sah, karena mereka tahu bahwa itu sangat sulit untuk menulis kode yang kuat dalam menghadapi reentrancy. "

Ya itu. Sangat sulit untuk menulis kode yang kuat dalam menghadapi reentrancy. Dan loop pesan bersarang memaksa Anda untuk menulis kode yang kuat dalam menghadapi reentrancy. Inilah sebabnya mengapa jawaban yang diterima (dan yang paling banyak dipilih) untuk pertanyaan ini sangat berbahaya dalam praktiknya.

Jika Anda benar-benar keluar dari semua opsi lain - Anda tidak dapat mendesain ulang kode Anda, Anda tidak dapat menata ulang menjadi async - Anda dipaksa oleh kode panggilan yang tidak dapat diubah untuk disinkronkan - Anda tidak dapat mengubah kode hilir menjadi sinkronisasi - Anda tidak dapat memblokir - Anda tidak dapat menjalankan kode async pada utas terpisah - saat itu dan baru Anda harus mempertimbangkan merangkul reentrancy.

Jika Anda menemukan diri Anda di sudut ini, saya akan merekomendasikan menggunakan sesuatu seperti Dispatcher.PushFrameuntuk aplikasi WPF , mengulang dengan Application.DoEventsuntuk aplikasi WinForm, dan untuk kasus umum, saya sendiri AsyncContext.Run.

Stephen Cleary
sumber
Stephen, ada sangat mirip lain qestion yang Anda berikan jawabannya mengagumkan juga. Apakah Anda pikir salah satu dari mereka dapat ditutup sebagai duplikat atau mungkin menggabungkan permintaan atau membuka meta terlebih dahulu (karena masing-masing q memiliki ~ 200.000 tampilan 200+ suara)? Saran?
Alexei Levenkov
1
@AlexeiLevenkov: Saya merasa tidak benar melakukan itu, karena beberapa alasan: 1) Jawaban pada pertanyaan terkait cukup ketinggalan zaman. 2) Saya telah menulis seluruh artikel tentang hal yang menurut saya lebih lengkap daripada SO Q / A yang ada. 3) Jawaban yang diterima untuk pertanyaan ini sangat populer. 4) Saya sangat menentang jawaban yang diterima itu. Jadi, menutup ini sebagai dup itu akan menjadi penyalahgunaan kekuasaan; menutup bahwa sebagai dup dari ini (atau penggabungan) akan memberdayakan jawaban yang berbahaya bahkan lebih. Saya membiarkannya, dan menyerahkannya kepada komunitas.
Stephen Cleary
Baik. Saya akan mempertimbangkan membawanya pada meta daripada dalam beberapa cara.
Alexei Levenkov
9
Jawaban ini jauh melampaui kepala saya. "Gunakan async sepanjang jalan" adalah saran yang membingungkan, karena jelas tidak mungkin untuk diikuti. Program dengan Main()metode async tidak dapat dikompilasi; pada titik tertentu Anda harus menjembatani kesenjangan antara dunia sinkronisasi dan dunia async. Ini bukan " situasi yang sangat langka" , itu perlu dalam setiap program yang memanggil metode async. Tidak ada opsi untuk tidak "melakukan sinkronisasi-asinkron" , hanya opsi untuk mengurangi beban itu hingga metode pemanggilan alih-alih memanggulnya dalam metode yang sedang Anda tulis.
Mark Amery
1
Bagus. Saya akan menggunakan asyncsemua metode dalam aplikasi saya sekarang. Dan itu banyak. Tidak bisakah ini hanya default?
ygoe
25

Jika saya membaca pertanyaan Anda dengan benar - kode yang ingin panggilan sinkron ke metode async dijalankan pada utas dispatcher yang ditangguhkan. Dan Anda ingin benar-benar secara bersamaan memblokir utas itu sampai metode async selesai.

Metode Async di C # 5 didukung oleh secara efektif memotong metode menjadi potongan-potongan di bawah tenda, dan mengembalikan Taskyang dapat melacak penyelesaian keseluruhan shabang keseluruhan. Namun, bagaimana metode cacah mengeksekusi dapat bergantung pada jenis ekspresi yang diteruskan ke awaitoperator.

Sebagian besar waktu, Anda akan menggunakan awaitekspresi tipe Task. Implementasi Task dari awaitpola adalah "pintar" karena tidak sesuai dengan SynchronizationContext, yang pada dasarnya menyebabkan hal berikut terjadi:

  1. Jika utas yang masuk awaitberada pada utas loop pesan Dispatcher atau WinForms, itu memastikan bahwa potongan metode async terjadi sebagai bagian dari pemrosesan antrian pesan.
  2. Jika utas yang masuk awaitberada pada utas pool ulir, maka potongan yang tersisa dari metode async terjadi di mana saja pada utas pool.

Itu sebabnya Anda mungkin mengalami masalah - implementasi metode async mencoba menjalankan sisanya pada Dispatcher - meskipun ditangguhkan.

.... mendukung! ....

Saya harus mengajukan pertanyaan, mengapa Anda mencoba untuk memblokir metode async secara sinkron? Melakukan hal itu akan mengalahkan tujuan mengapa metode ingin dipanggil secara tidak sinkron. Secara umum, ketika Anda mulai menggunakan awaitmetode Dispatcher atau UI, Anda ingin mengubah seluruh aliran UI Anda sebagai sinkronisasi. Misalnya, jika callstack Anda mirip dengan yang berikut:

  1. [Teratas] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()- WPFatau WinFormsKode
  6. [Message Loop] - WPFatau WinFormsLoop Pesan

Kemudian setelah kode diubah untuk menggunakan async, biasanya Anda akan berakhir dengan

  1. [Teratas] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()- WPFatau WinFormsKode
  6. [Message Loop] - WPFatau WinFormsLoop Pesan

Sebenarnya Menjawab

Kelas AsyncHelpers di atas benar-benar berfungsi karena berperilaku seperti loop pesan bersarang, tetapi menginstal mekanik paralelnya sendiri ke Dispatcher daripada mencoba mengeksekusi pada Dispatcher itu sendiri. Itu satu solusi untuk masalah Anda.

Solusi lain adalah dengan mengeksekusi metode async Anda pada utas threadpool, dan kemudian menunggu sampai selesai. Melakukannya mudah - Anda dapat melakukannya dengan cuplikan berikut:

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

API terakhir adalah Task.Run (...), tetapi dengan CTP Anda akan membutuhkan akhiran Ex ( penjelasan di sini ).

Theo Yaung
sumber
+1 untuk penjelasan terperinci, namun TaskEx.RunEx(GetCustomers).Resultmenggantung aplikasi saat dijalankan pada utas dispatcher yang ditangguhkan. Juga, metode GetCustomers () biasanya dijalankan async, namun dalam satu situasi perlu dijalankan secara sinkron, jadi saya mencari cara untuk melakukannya tanpa membangun versi sinkronisasi dari metode tersebut.
Rachel
+1 untuk "mengapa Anda mencoba untuk memblokir metode async secara sinkron?" Selalu ada cara untuk menggunakan asyncmetode dengan benar ; loop bersarang tentu harus dihindari.
Stephen Cleary
24

Ini bekerja dengan baik untuk saya

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;
    }
}
Sejuk
sumber
Anda juga perlu menggunakan metode Task.Unwrap , karena pernyataan Task.Wait Anda menyebabkan menunggu Task luar (dibuat oleh Task.Run ), bukan untuk menunggu dalam Tugas t diteruskan sebagai parameter metode ekstensi. Metode Task.Run Anda mengembalikan bukan Task <T>, tetapi Task <Task <T>>. Dalam beberapa skenario sederhana, solusi Anda mungkin berfungsi karena optimasi TaskScheduler, misalnya menggunakan metode TryExecuteTaskInline untuk mengeksekusi Tugas dalam utas saat ini selama operasi Tunggu. Silakan lihat komentar saya untuk jawaban ini .
sgnsajgon
1
Itu tidak benar. Task.Run akan mengembalikan Task <T>. Lihat ini msdn.microsoft.com/en-us/library/hh194918(v=vs.110).aspx
Clement
Bagaimana ini seharusnya digunakan? Kebuntuan ini di WPF:MyAsyncMethod().RunTaskSynchronously();
ygoe
18

Cara paling sederhana yang saya temukan untuk menjalankan tugas secara sinkron dan tanpa memblokir utas UI adalah dengan menggunakan RunSynchronously () seperti:

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

Dalam kasus saya, saya memiliki peristiwa yang menyala ketika sesuatu terjadi. Saya tidak tahu berapa kali itu akan terjadi. Jadi, saya menggunakan kode di atas dalam acara saya, jadi setiap kali menyala, ia membuat tugas. Tugas dieksekusi secara serempak dan itu bekerja dengan baik untuk saya. Saya hanya terkejut bahwa butuh waktu lama bagi saya untuk mengetahui hal ini mengingat betapa sederhananya itu. Biasanya, rekomendasi jauh lebih kompleks dan rawan kesalahan. Ini sederhana dan bersih.

pixel
sumber
1
Tetapi bagaimana kita bisa menggunakan metode ini ketika kode async mengembalikan sesuatu yang kita butuhkan?
S.Serpooshan
16

Saya sudah menghadapinya beberapa kali, kebanyakan dalam pengujian unit atau dalam pengembangan layanan windows. Saat ini saya selalu menggunakan fitur ini:

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

Sederhana, mudah, dan saya tidak punya masalah.

J. Lennon
sumber
Ini adalah satu-satunya yang tidak menemui jalan buntu bagi saya.
AndreFeijo
15

Saya menemukan kode ini di komponen Microsoft.AspNet.Identity.Core, dan berfungsi.

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

Hanya sedikit catatan - pendekatan ini:

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

bekerja untuk WinRT.

Biarkan saya jelaskan:

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

Selain itu pendekatan ini hanya berfungsi untuk solusi Windows Store!

Catatan: Cara ini tidak aman untuk thread jika Anda memanggil metode Anda di dalam metode async lainnya (menurut komentar dari @Servy)

RredCat
sumber
Saya menjelaskan solusi ini, periksa bagian EDIT.
RredCat
2
Ini dapat dengan mudah menghasilkan kebuntuan ketika dipanggil dalam situasi asinkron.
Servy
@Servy masuk akal. Jadi ketika saya mendapatkan benar menggunakan Tunggu (timeOut) dapat membantu, bukan?
RredCat
1
Maka Anda perlu khawatir tentang batas waktu tercapai ketika operasi tidak benar-benar dilakukan, yang sangat buruk, dan juga waktu yang dihabiskan menunggu sampai batas waktu dalam kasus di mana deadlock (dan dalam hal ini Anda masih melanjutkan kapan tidak dilakukan). Jadi tidak, itu tidak memperbaiki masalah.
Servy
@Servy Sepertinya saya harus menerapkan CancellationTokensolusi saya.
RredCat
10

Dalam kode Anda, tugas tunggu pertama Anda untuk dieksekusi tetapi Anda belum memulainya sehingga menunggu tanpa batas. Coba ini:

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

Edit:

Anda mengatakan bahwa Anda mendapatkan pengecualian. Silakan kirim detail lebih lanjut, termasuk jejak tumpukan.
Mono berisi test case berikut:

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

        Assert.AreEqual (1, val);
}

Periksa apakah ini cocok untuk Anda. Jika tidak, meskipun sangat tidak mungkin, Anda mungkin memiliki CTP Async build yang aneh. Jika berhasil, Anda mungkin ingin memeriksa apa yang sebenarnya dihasilkan oleh kompiler dan bagaimana Taskinstantiasi berbeda dari sampel ini.

Edit # 2:

Aku memeriksa dengan reflektor bahwa pengecualian Anda dijelaskan terjadi ketika m_actionadalah null. Ini agak aneh, tapi saya bukan ahli CTP Async. Seperti yang saya katakan, Anda harus mendekompilasi kode Anda dan melihat bagaimana sebenarnya Tasksedang dipakai bagaimana pun m_actionitu null.


PS Apa masalahnya dengan downvotes sesekali? Peduli menguraikan?

Dan Abramov
sumber
Saya menyesuaikan pertanyaan saya untuk membuat kode yang saya coba sedikit lebih jelas. RunSynchronously mengembalikan kesalahan RunSynchronously may not be called on a task unbound to a delegate. Google tidak membantu karena semua hasil untuk itu dalam bahasa Cina ...
Rachel
Saya pikir perbedaannya adalah bahwa saya tidak membuat Tugas dan kemudian mencoba menjalankannya. Sebagai gantinya, tugas tersebut dibuat oleh metode async saat awaitkata kunci digunakan. Pengecualian yang diposting dalam komentar saya sebelumnya adalah pengecualian yang saya dapatkan, meskipun itu adalah salah satu dari sedikit yang saya tidak bisa Google dan menemukan penyebab atau resolusi untuk.
Rachel
1
asyncdan asynckata kunci tidak lebih dari gula sintaksis. Compiler menghasilkan kode untuk membuat Task<Customer>di GetCustomers()jadi itulah di mana saya akan melihat pertama. Adapun pengecualian, Anda hanya memposting pesan pengecualian, yang tidak berguna tanpa tipe pengecualian dan jejak stack. Sebut ToString()metode pengecualian dan poskan output dalam pertanyaan.
Dan Abramov
@gaearon: Saya memposting detail pengecualian dan menumpuk jejak di pertanyaan awal saya.
Rachel
2
@gaearon Saya pikir Anda mendapat downvotes karena posting Anda tidak berlaku untuk pertanyaan. Diskusi ini tentang metode menunggu async, bukan tentang metode pengembalian tugas yang sederhana. Selain itu, menurut pendapat saya, mekanisme menunggu-async adalah sintaksis, tetapi tidak terlalu sepele - ada kelanjutan, pengambilan konteks, dilanjutkannya konteks lokal, penanganan pengecualian lokal yang ditingkatkan, dan banyak lagi. Kemudian, Anda tidak boleh memanggil metode RunSynchronously pada hasil dari metode async, karena menurut definisi metode asynchronous harus mengembalikan Tugas yang saat ini setidaknya dijadwalkan, dan lebih dari sekali dalam keadaan berjalan.
sgnsajgon
9

Diuji dalam .Net 4.6. Itu juga dapat menghindari kebuntuan.

Untuk metode async kembali Task.

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

Untuk metode async kembali Task<T>

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

Edit :

Jika pemanggil berjalan di utas kumpulan utas (atau penelepon juga dalam tugas), itu mungkin masih menyebabkan kebuntuan dalam beberapa situasi.

Liang
sumber
1
Jawaban saya setelah hampir 8 tahun :) Contoh kedua - akan menghasilkan kebuntuan dalam semua konteks terjadwal yang terutama digunakan (aplikasi konsol / .NET core / aplikasi desktop / ...). di sini Anda memiliki ikhtisar lebih banyak tentang apa yang saya bicarakan sekarang: medium.com/rubrikkgroup/…
W92
Resultsangat cocok untuk pekerjaan itu jika Anda menginginkan panggilan sinkron, dan sebaliknya berbahaya. Tidak ada dalam nama Resultatau intellisense Resultyang menunjukkan itu adalah panggilan yang memblokir. Itu benar-benar harus diganti namanya.
Zodman
5

gunakan snip kode di bawah ini

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

Mengapa tidak membuat panggilan seperti:

Service.GetCustomers();

itu bukan async.

Daniel A. White
sumber
4
Itu akan menjadi apa yang saya lakukan jika saya tidak bisa menjalankan ini ... membuat versi Sync selain versi Async
Rachel
3

Jawaban ini dirancang untuk siapa saja yang menggunakan WPF untuk .NET 4.5.

Jika Anda mencoba mengeksekusi Task.Run()di utas GUI, maka task.Wait()akan hang tanpa batas, jika Anda tidak memiliki asynckata kunci dalam definisi fungsi Anda.

Metode ekstensi ini memecahkan masalah dengan memeriksa untuk melihat apakah kita berada di utas GUI, dan jika demikian, menjalankan tugas pada utas pengirim WPF.

Kelas ini dapat bertindak sebagai perekat antara dunia async / await dan dunia non-async / await, dalam situasi di mana ia tidak dapat dihindari, seperti properti MVVM atau dependensi pada API lain yang tidak menggunakan async / tunggu.

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

Cukup menelepon .Result;atau .Wait()merupakan risiko untuk kebuntuan seperti yang banyak dikatakan dalam komentar. Karena sebagian besar dari kita suka oneliners Anda dapat menggunakannya untuk.Net 4.5<

Memperoleh nilai melalui metode async:

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

Sinkron memanggil metode async

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

Tidak ada masalah jalan buntu yang akan terjadi karena penggunaan Task.Run.

Sumber:

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

Ogglas
sumber
1

Saya pikir metode pembantu berikut ini juga bisa menyelesaikan masalah.

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

Dapat digunakan dengan cara berikut:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
donttellya
sumber
1
Tolong jelaskan pemungutan suara
donttellya
2
... Saya masih sangat tertarik mengapa jawaban ini tidak dipilih?
donttellya
Ini bukan "sinkron" yang sebenarnya. Anda dapat membuat dua utas dan menunggu hasil pertama dari yang lain.
tmt
dan selain itu, ini adalah ide yang sangat buruk.
Dan Pantry
1
Saya baru saja menulis kode yang hampir sama (baris demi baris sama) tetapi alih-alih menggunakan SemaphoreSlim alih-alih acara reset otomatis. Seandainya aku melihat ini lebih cepat. Saya menemukan pendekatan ini untuk mencegah kebuntuan dan menjaga kode async Anda tetap sama seperti dalam skenario asinkron yang sebenarnya. Tidak begitu yakin mengapa ini adalah ide yang buruk. Tampaknya jauh lebih bersih daripada pendekatan lain yang saya lihat di atas.
tmrog
0

Ini bekerja untuk saya

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();
        }
    }
}
Dan Nguyen
sumber
-1

Saya telah menemukan bahwa SpinWait bekerja dengan sangat baik untuk ini.

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;

Pendekatan di atas tidak perlu menggunakan .Result atau. Tunggu (). Ini juga memungkinkan Anda menentukan batas waktu agar Anda tidak terjebak selamanya jika tugas tidak pernah selesai.

Curtis
sumber
1
Downvote menyarankan seseorang tidak menyukai metode ini. Apakah ada seseorang yang bisa berkomentar tentang kelemahannya?
Grax32
Dengan tidak adanya downvoter yang mengatakan MENGAPA downvote diberikan, dapatkah seseorang meneguhkannya? :-)
Curtis
1
Ini adalah polling (pemintalan), delegasi akan mengambil utas dari kolam hingga 1000 kali per detik. Ini mungkin tidak mengembalikan kontrol segera setelah tugas selesai (hingga 10 + ms kesalahan). Jika selesai oleh batas waktu tugas akan terus berjalan, yang membuat batas waktu praktis tidak berguna.
Sinatr
Sebenarnya, saya menggunakan ini di semua tempat dalam kode saya dan ketika kondisi terpenuhi, SpinWaitSpinUntil () segera keluar. Jadi, mana yang lebih dulu, 'syarat bertemu' atau batas waktu, tugas keluar. Itu tidak terus berjalan.
Curtis
-3

Pada wp8:

Bungkus itu:

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

Sebut saja:

GetCustomersSynchronously();
pengguna2113284
sumber
3
Tidak, ini tidak akan berhasil, karena tugas tidak menunggu delegasi dari konstruktor (ini delegasi dan bukan tugas ..)
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;
        }
    }
ksemenenko
sumber
-5

Atau Anda bisa pergi dengan:

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

Agar ini dikompilasi, pastikan Anda merujuk perakitan ekstensi:

System.Net.Http.Formatting
pengguna2057962
sumber
-9

Coba kode berikut ini berfungsi untuk saya:

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);
    }
}
gandhraj gayakwad
sumber