Saya baru mengenal pemrograman asinkron dengan async
pengubah. Saya mencoba mencari cara untuk memastikan bahwa Main
metode aplikasi konsol saya benar-benar berjalan secara tidak sinkron.
class Program
{
static void Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = bs.GetList();
}
}
public class Bootstrapper {
public async Task<List<TvChannel>> GetList()
{
GetPrograms pro = new GetPrograms();
return await pro.DownloadTvChannels();
}
}
Saya tahu ini tidak berjalan secara tidak sinkron dari "atas." Karena tidak mungkin untuk menentukan async
pengubah pada Main
metode, bagaimana saya bisa menjalankan kode secara tidak main
sinkron?
c#
.net
asynchronous
console-application
danielovich
sumber
sumber
Jawaban:
Seperti yang Anda temukan, di VS11 kompiler akan melarang
async Main
metode. Ini diizinkan (tapi tidak pernah disarankan) di VS2010 dengan CTP Async.Saya memiliki posting blog baru-baru ini tentang program konsol async / await dan asynchronous pada khususnya. Berikut beberapa informasi latar belakang dari pos pengantar:
Inilah mengapa ini menjadi masalah dalam program Konsol dengan
async Main
:Salah satu solusinya adalah menyediakan konteks Anda sendiri - "loop utama" untuk program konsol Anda yang kompatibel dengan async.
Jika Anda memiliki mesin dengan CTP Async, Anda dapat menggunakan
GeneralThreadAffineContext
dari My Documents \ Microsoft Visual Studio Async CTP \ Sampel (C # Testing) Unit Testing \ AsyncTestUtilities . Atau, Anda dapat menggunakanAsyncContext
dari paket NuGet Nito.AsyncEx saya .Berikut ini contoh menggunakan
AsyncContext
;GeneralThreadAffineContext
memiliki penggunaan yang hampir identik:Atau, Anda bisa memblokir utas Konsol utama sampai pekerjaan asinkron Anda selesai:
Perhatikan penggunaan
GetAwaiter().GetResult()
; ini menghindariAggregateException
pembungkus yang terjadi jika Anda menggunakanWait()
atauResult
.Pembaruan, 2017-11-30: Pada Visual Studio 2017 Pembaruan 3 (15,3), bahasa sekarang mendukung
async Main
- selama kembaliTask
atauTask<T>
. Jadi sekarang Anda dapat melakukan ini:Semantik tampaknya sama dengan
GetAwaiter().GetResult()
gaya memblokir utas. Namun, belum ada spesifikasi bahasa untuk C # 7.1, jadi ini hanya asumsi.sumber
Wait
atauResult
, dan tidak ada yang salah dengan itu. Tetapi perlu diketahui bahwa ada dua perbedaan penting: 1) semuaasync
kelanjutan berjalan di kolam utas daripada utas utama, dan 2) setiap pengecualian dibungkus dalamAggregateException
.<LangVersion>latest</LangVersion>
ke dalam file csproj, seperti yang ditunjukkan di sini .Anda dapat menyelesaikan ini dengan konstruksi sederhana ini:
Itu akan menempatkan semua yang Anda lakukan di ThreadPool di tempat yang Anda inginkan (jadi Tugas lain yang Anda mulai / tunggu jangan mencoba untuk bergabung kembali dengan Thread yang seharusnya tidak mereka lakukan), dan tunggu sampai semuanya selesai sebelum menutup aplikasi Konsol. Tidak perlu untuk loop khusus atau lib luar.
Sunting: Memasukkan solusi Andrew untuk Pengecualian yang tidak tertangkap.
sumber
Wait()
denganGetAwaiter().GetResult()
Anda akan menghindariAggregateException
pembungkus ketika hal-hal melempar.async main
diperkenalkan di C # 7.1, pada penulisan ini.Anda dapat melakukan ini tanpa perlu perpustakaan eksternal juga dengan melakukan hal berikut:
sumber
getListTask.Result
itu juga merupakan panggilan pemblokiran sehingga kode di atas dapat ditulis tanpaTask.WaitAll(getListTask)
.GetList
melempar, Anda harus menangkapAggregateException
dan menginterogasi pengecualiannya untuk menentukan pengecualian yang sebenarnya dilemparkan. Anda dapat, bagaimanapun, panggilanGetAwaiter()
untuk mendapatkanTaskAwaiter
untuk ituTask
, dan memanggilGetResult()
itu, yaituvar list = getListTask.GetAwaiter().GetResult();
. Saat mendapatkan hasil dariTaskAwaiter
(juga panggilan pemblokiran) pengecualian apa pun yang dilemparkan tidak akan dibungkus denganAggregateException
.Dalam C # 7.1 Anda akan dapat melakukan Main async yang tepat . Tanda tangan yang sesuai untuk
Main
metode telah diperluas ke:Misalnya Anda bisa melakukan:
Pada waktu kompilasi, metode titik masuk async akan diterjemahkan ke panggilan
GetAwaitor().GetResult()
.Detail: https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main
EDIT:
Untuk mengaktifkan fitur bahasa C # 7.1, Anda perlu klik kanan pada proyek dan klik "Properties" lalu pergi ke tab "Build". Di sana, klik tombol lanjutan di bagian bawah:
Dari menu tarik-turun versi bahasa, pilih "7.1" (atau nilai yang lebih tinggi):
Standarnya adalah "versi utama terbaru" yang akan mengevaluasi (pada saat penulisan ini) ke C # 7.0, yang tidak mendukung async utama di aplikasi konsol.
sumber
Saya akan menambahkan fitur penting yang diabaikan semua jawaban lainnya: pembatalan.
Salah satu hal besar dalam TPL adalah dukungan pembatalan, dan aplikasi konsol memiliki metode pembatalan bawaan (CTRL + C). Sangat mudah untuk mengikat mereka bersama. Ini adalah bagaimana saya menyusun semua aplikasi konsol async saya:
sumber
Wait()
juga?Wait()
, kode async tidak akan menunggu sampai selesai - itu akan berhenti menunggu dan segera mengakhiri proses.Wait()
metode ini dilewati token yang sama. Yang ingin saya katakan adalah, sepertinya tidak ada bedanya.C # 7.1 (menggunakan vs 2017 pembaruan 3) memperkenalkan async main
Kamu bisa menulis:
Untuk detail lebih lanjut C # 7 Series, Bagian 2: Async Main
Memperbarui:
Anda mungkin mendapatkan kesalahan kompilasi:
Kesalahan ini disebabkan bahwa vs2017.3 dikonfigurasi secara default sebagai c # 7.0 bukan c # 7.1.
Anda harus secara eksplisit mengubah pengaturan proyek Anda untuk menetapkan fitur c # 7.1.
Anda dapat mengatur c # 7.1 dengan dua metode:
Metode 1: Menggunakan jendela pengaturan proyek:
Metode2: Ubah PropertyGroup dari .csproj secara manual
Tambahkan properti ini:
contoh:
sumber
Jika Anda menggunakan C # 7.1 atau lebih baru, pergilah dengan jawaban nawfal dan cukup ubah jenis pengembalian metode Utama Anda ke
Task
atauTask<int>
. Jika tidak:async Task MainAsync
seperti kata Johan ..GetAwaiter().GetResult()
untuk menangkap pengecualian yang mendasarinya seperti kata do0g .CTRL+C
harus segera menghentikan proses. (Terima kasih binki !)OperationCancelledException
- kembalikan kode kesalahan yang sesuai.Kode akhir terlihat seperti:
sumber
e.Cancel = true
tidak bersyarat.Belum terlalu membutuhkan ini, tetapi ketika saya telah menggunakan aplikasi konsol untuk tes cepat dan diperlukan async saya baru saja menyelesaikannya seperti ini:
sumber
SynchronizationContext
terkait dengan utas utama. Jadi itu tidak akan menemui jalan buntu karena bahkan tanpaConfigureAwait(false)
, semua kelanjutan akan dijalankan di threadpool.Untuk tugas panggilan yang tidak sinkron dari Utama, gunakan
Tugas.Jalankan () untuk .NET 4.5
Task.Factory.StartNew () untuk .NET 4.0 (Mungkin memerlukan perpustakaan Microsoft.Bcl.Async untuk async dan menunggu kata kunci)
Detail: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
sumber
Di Utama, coba ubah panggilan ke GetList ke:
sumber
Ketika C # 5 CTP diperkenalkan, Anda tentu bisa menandai Main dengan
async
... meskipun itu umumnya bukan ide yang baik untuk melakukannya. Saya percaya ini diubah oleh rilis VS 2013 menjadi kesalahan.Kecuali jika Anda telah memulai utas latar depan lainnya , program Anda akan keluar saat
Main
selesai, bahkan jika itu memulai beberapa pekerjaan latar belakang.Apa yang sebenarnya ingin Anda lakukan? Perhatikan bahwa
GetList()
metode Anda benar-benar tidak perlu async saat ini - itu menambahkan lapisan tambahan tanpa alasan nyata. Secara logis setara dengan (tetapi lebih rumit dari):sumber
DownloadTvChannels()
dikembalikan? Mungkin mengembalikannyaTask<List<TvChannel>>
bukan? Jika tidak, kecil kemungkinan Anda akan bisa menunggu. (Kemungkinan, mengingat pola awaiter, tapi tidak mungkin.) AdapunMain
metode - masih perlu statis ... kau ganti denganstatic
modifier denganasync
modifier mungkin?public static async void Main() {}
? Tetapi jikaDownloadTvChannels()
sudah mengembalikanTask<List<TvChannel>>
, mungkin sudah asinkron - jadi Anda tidak perlu menambahkan lapisan lain. Perlu dipahami dengan cermat.Versi terbaru dari C # - C # 7.1 memungkinkan untuk membuat aplikasi konsol async. Untuk mengaktifkan C # 7.1 dalam proyek, Anda harus meningkatkan VS Anda ke setidaknya 15,3, dan mengubah versi C # ke
C# 7.1
atauC# latest minor version
. Untuk melakukannya, buka Properti proyek -> Bangun -> Tingkat Lanjut -> Versi bahasa.Setelah ini, kode berikut akan berfungsi:
sumber
Pada MSDN, dokumentasi untuk Metode Task.Run (Tindakan) memberikan contoh ini yang menunjukkan cara menjalankan metode secara asinkron dari
main
:Perhatikan pernyataan ini yang mengikuti contoh:
Jadi, jika Anda ingin tugas dijalankan di utas aplikasi utama, lihat jawabannya oleh @StephenCleary .
Dan mengenai utas tempat tugas itu berjalan, perhatikan juga komentar Stephen tentang jawabannya:
(Lihat Penanganan Pengecualian (Perpustakaan Tugas Paralel) untuk cara menggabungkan penanganan pengecualian untuk menangani
AggregateException
.)Akhirnya, di MSDN dari dokumentasi untuk Metode Task.Delay (TimeSpan) , contoh ini menunjukkan cara menjalankan tugas asinkron yang mengembalikan nilai:
Perhatikan bahwa alih-alih meneruskan
delegate
keTask.Run
, Anda dapat meneruskan fungsi lambda seperti ini:sumber
Untuk menghindari pembekuan ketika Anda memanggil suatu fungsi di suatu tempat di tumpukan panggilan yang mencoba untuk bergabung kembali dengan utas saat ini (yang terjebak dalam Tunggu), Anda perlu melakukan hal berikut:
(Para pemain hanya diminta untuk menyelesaikan ambiguitas)
sumber
Dalam kasus saya, saya memiliki daftar pekerjaan yang ingin saya jalankan di async dari metode utama saya, telah menggunakan ini dalam produksi selama beberapa waktu dan berfungsi dengan baik.
sumber