Pada C # 7.0, metode async dapat mengembalikan ValueTask <T>. Penjelasan mengatakan bahwa itu harus digunakan ketika kita memiliki hasil cache atau mensimulasikan async melalui kode sinkron. Namun saya masih tidak mengerti apa masalah dengan menggunakan ValueTask selalu atau bahkan mengapa async / menunggu tidak dibangun dengan tipe nilai sejak awal. Kapan ValueTask gagal melakukan pekerjaan itu?
c#
asynchronous
Stilgar
sumber
sumber
ValueTask<T>
(dalam hal alokasi) tidak terwujud untuk operasi yang sebenarnya tidak sinkron (karena dalam kasusValueTask<T>
itu masih perlu alokasi tumpukan). Ada juga masalahTask<T>
memiliki banyak dukungan lain di perpustakaan.Jawaban:
Dari dokumen API (penekanan ditambahkan):
sumber
Task
alokasi tunggal (yang kecil dan murah hari ini), tetapi dengan biaya membuat alokasi pemanggil yang ada lebih besar dan menggandakan ukuran nilai kembali (berdampak pada alokasi register). Meskipun ini merupakan pilihan yang jelas untuk skenario baca buffered, menerapkannya secara default ke semua antarmuka bukanlah sesuatu yang saya sarankan.Task
atauValueTask
dapat digunakan sebagai tipe pengembalian sinkron (denganTask.FromResult
). Tetapi masih ada nilai (heh) diValueTask
jika Anda memiliki sesuatu yang Anda harapkan akan sinkron.ReadByteAsync
menjadi contoh klasik. Saya percayaValueTask
diciptakan terutama untuk "saluran" baru (byte level rendah stream), mungkin juga digunakan dalam inti ASP.NET di mana kinerja sangat penting.Task<T>
sebagai default. Ini hanya karena sebagian besar pengembang tidak terbiasa dengan pembatasan sekitarValueTask<T>
(khususnya, aturan "hanya konsumsi sekali" dan aturan "tidak ada pemblokiran"). Yang mengatakan, jika semua pengembang di tim Anda baikValueTask<T>
, maka saya akan merekomendasikan pedoman tingkat tim lebih sukaValueTask<T>
.Jenis Struct tidak gratis. Menyalin struct yang lebih besar dari ukuran referensi bisa lebih lambat daripada menyalin referensi. Menyimpan struct yang lebih besar dari referensi membutuhkan lebih banyak memori daripada menyimpan referensi. Structs yang lebih besar dari 64 bit mungkin tidak terdaftar ketika referensi bisa didaftarkan. Manfaat dari tekanan pengumpulan yang lebih rendah mungkin tidak melebihi biaya.
Masalah kinerja harus didekati dengan disiplin teknik. Buat tujuan, ukur kemajuan Anda terhadap sasaran, dan kemudian putuskan bagaimana memodifikasi program jika sasaran tidak terpenuhi, mengukur sepanjang jalan untuk memastikan bahwa perubahan Anda benar-benar perbaikan.
await
telah ditambahkan ke C # lama setelahTask<T>
tipe sudah ada. Akan agak aneh untuk menciptakan tipe baru ketika sudah ada. Danawait
melewati banyak iterasi desain sebelum memutuskan yang dikirimkan pada tahun 2012. Yang sempurna adalah musuh dari yang baik; lebih baik untuk mengirimkan solusi yang berfungsi baik dengan infrastruktur yang ada dan kemudian jika ada permintaan pengguna, berikan perbaikan nanti.Saya perhatikan juga bahwa fitur baru yang memungkinkan tipe yang dipasok pengguna menjadi output dari metode yang dibuat oleh kompiler menambah risiko dan beban pengujian. Ketika satu-satunya hal yang dapat Anda kembalikan adalah batal atau tugas, tim pengujian tidak harus mempertimbangkan skenario apa pun di mana beberapa tipe yang benar-benar gila dikembalikan. Menguji kompiler berarti mencari tahu tidak hanya program apa yang orang mungkin tulis, tetapi program apa yang mungkin ditulis, karena kami ingin kompiler mengkompilasi semua program legal, bukan hanya semua program yang masuk akal. Itu mahal.
Tujuan dari hal ini adalah peningkatan kinerja. Itu tidak melakukan pekerjaan jika tidak meningkatkan kinerja secara terukur dan signifikan . Tidak ada jaminan bahwa itu akan terjadi.
sumber
ValueTask<T>
bukan bagian dariTask<T>
, itu superset .Jadi itu memungkinkan Anda menulis satu metode yang asinkron atau sinkron, daripada menulis satu metode yang identik untuk masing-masing. Anda bisa menggunakannya di mana saja Anda gunakan
Task<T>
tetapi seringkali tidak akan menambahkan apa pun.Yah, itu menambahkan satu hal: Ini menambahkan janji tersirat kepada pemanggil bahwa metode ini benar-benar menggunakan fungsionalitas tambahan yang
ValueTask<T>
menyediakan. Saya pribadi lebih suka memilih parameter dan mengembalikan tipe yang memberi tahu penelepon sebanyak mungkin. Jangan kembaliIList<T>
jika enumerasi tidak dapat memberikan hitungan; jangan kembaliIEnumerable<T>
jika itu bisa. Konsumen Anda seharusnya tidak perlu mencari dokumentasi apa pun untuk mengetahui metode mana yang dapat disebut secara serempak dan mana yang tidak.Saya tidak melihat perubahan desain di masa depan sebagai argumen yang meyakinkan di sana. Justru sebaliknya: Jika suatu metode mengubah semantiknya, itu harus memecah bangunan sampai semua panggilan ke itu diperbarui sesuai. Jika itu dianggap tidak diinginkan (dan percayalah, saya bersimpati pada keinginan untuk tidak merusak build), pertimbangkan versi antarmuka.
Ini pada dasarnya adalah tujuan dari pengetikan yang kuat.
Jika beberapa pemrogram yang merancang metode async di toko Anda tidak dapat membuat keputusan berdasarkan informasi, mungkin akan membantu jika menugaskan mentor senior untuk masing-masing pemrogram yang kurang berpengalaman dan melakukan peninjauan kode mingguan. Jika mereka salah menebak, jelaskan mengapa itu harus dilakukan secara berbeda. Ini overhead untuk orang-orang senior, tapi itu akan membuat junior lebih cepat lebih cepat daripada hanya melemparkan mereka di ujung yang dalam dan memberi mereka beberapa aturan sewenang-wenang untuk diikuti.
Jika orang yang menulis metode itu tidak tahu apakah itu bisa disebut secara serempak, siapa di Bumi yang melakukannya ?!
Jika Anda memiliki banyak programmer yang tidak berpengalaman yang menulis metode async, apakah mereka juga memanggil mereka? Apakah mereka memenuhi syarat untuk mencari tahu sendiri mana yang aman untuk disebut async, atau akankah mereka mulai menerapkan aturan arbitrer yang serupa dengan cara mereka menyebut hal-hal ini?
Masalahnya di sini bukan tipe pengembalian Anda, itu adalah programer yang dimasukkan ke dalam peran yang belum siap. Itu pasti terjadi karena suatu alasan, jadi saya yakin itu tidak mudah untuk diperbaiki. Menggambarkannya tentu bukan solusi. Tetapi mencari cara untuk menyelinap masalah melewati kompiler juga bukan solusi.
sumber
ValueTask<T>
, saya berasumsi bahwa orang yang menulis metode melakukannya karena metode tersebut sebenarnya menggunakan fungsi yangValueTask<T>
menambahkan. Saya tidak mengerti mengapa Anda pikir semua metode Anda diinginkan untuk memiliki tipe pengembalian yang sama. Apa tujuannya di sana?object
karena mungkin suatu hari nanti Anda akan ingin mereka untuk kembaliint
bukanstring
.Ada beberapa perubahan di .Net Core 2.1 . Mulai dari .net core 2.1 ValueTask tidak hanya dapat mewakili tindakan yang disinkronkan yang diselesaikan tetapi juga async yang diselesaikan. Selain itu kami menerima jenis non-generik
ValueTask
.Saya akan meninggalkan komentar Stephen Toub yang terkait dengan pertanyaan Anda:
Fitur dapat digunakan tidak hanya di .net core 2.1. Anda akan dapat menggunakannya dengan paket System.Threading.Tasks.Extensions .
sumber