Cara menggunakan ValueTask di C #

Pemrograman asinkron telah digunakan untuk beberapa waktu sekarang. Dalam beberapa tahun terakhir, ini telah dibuat lebih kuat dengan pengenalan kata kunci async dan await. Anda dapat memanfaatkan pemrograman asinkron untuk meningkatkan respons dan throughput aplikasi Anda.

Jenis pengembalian yang disarankan dari metode asinkron di C # adalah Tugas. Anda harus mengembalikan Task jika Anda ingin menulis metode asynchronous yang mengembalikan nilai. Jika Anda ingin menulis event handler, Anda dapat mengembalikan void. Hingga C # 7.0, metode asinkron dapat mengembalikan Tugas, Tugas, atau batal. Dimulai dengan C # 7.0, metode asinkron juga dapat mengembalikan ValueTask (tersedia sebagai bagian dari paket System.Threading.Tasks.Extensions) atau ValueTask. Artikel ini menyajikan diskusi tentang bagaimana kita dapat bekerja dengan ValueTask di C #.

Untuk bekerja dengan contoh kode yang disediakan dalam artikel ini, Anda harus menginstal Visual Studio 2019 di sistem Anda. Jika Anda belum memiliki salinannya, Anda dapat mengunduh Visual Studio 2019 di sini.

Buat proyek aplikasi konsol inti .NET di Visual Studio .NET

Pertama, mari buat proyek aplikasi konsol .NET Core di Visual Studio. Dengan asumsi Visual Studio 2019 diinstal di sistem Anda, ikuti langkah-langkah yang diuraikan di bawah ini untuk membuat proyek aplikasi konsol .NET Core baru di Visual Studio.

  1. Luncurkan Visual Studio IDE.
  2. Klik "Buat proyek baru".
  3. Di jendela "Buat proyek baru", pilih "Aplikasi Konsol (.NET Core)" dari daftar template yang ditampilkan.
  4. Klik Next.
  5. Di jendela "Configure your new project" yang ditampilkan berikutnya, tentukan nama dan lokasi untuk proyek baru tersebut.
  6. Klik Buat.

Ini akan membuat proyek aplikasi konsol .NET Core baru di Visual Studio 2019. Kami akan menggunakan proyek ini untuk menggambarkan penggunaan ValueTask di bagian selanjutnya dari artikel ini.

Mengapa saya harus menggunakan ValueTask?

Sebuah Tugas merepresentasikan status beberapa operasi, yaitu apakah operasi tersebut selesai, dibatalkan, dan seterusnya. Metode asynchronous dapat mengembalikan Task atau ValueTask.

Sekarang, karena Task adalah tipe referensi, mengembalikan objek Task dari metode asynchronous berarti mengalokasikan objek pada heap terkelola setiap kali metode dipanggil. Jadi, satu peringatan dalam menggunakan Task adalah Anda perlu mengalokasikan memori di heap terkelola setiap kali Anda mengembalikan objek Task dari metode Anda. Jika hasil operasi yang dilakukan oleh metode Anda tersedia segera atau diselesaikan secara bersamaan, alokasi ini tidak diperlukan dan karenanya menjadi mahal.

Di sinilah tepatnya tempat ValueTask datang untuk menyelamatkan. ValueTask memberikan dua manfaat utama. Pertama, ValueTask meningkatkan kinerja karena tidak memerlukan alokasi heap, dan kedua, mudah dan fleksibel untuk diterapkan. Dengan mengembalikan ValueTask alih-alih Task dari metode asynchronous ketika hasilnya segera tersedia, Anda dapat menghindari overhead alokasi yang tidak perlu karena "T" di sini mewakili struktur dan struct di C # adalah jenis nilai (berbeda dengan "T" di Task, yang mewakili kelas).

Task dan ValueTask mewakili dua tipe utama "yang dapat menunggu" di C #. Perhatikan bahwa Anda tidak dapat memblokir di ValueTask. Jika perlu memblokir, Anda harus mengonversi ValueTask menjadi Tugas menggunakan metode AsTask, lalu memblokir objek Tugas referensi tersebut.

Perhatikan juga bahwa setiap ValueTask hanya dapat dikonsumsi sekali. Di sini kata "mengkonsumsi" menyiratkan bahwa ValueTask dapat secara asinkron menunggu (menunggu) operasi selesai atau memanfaatkan AsTask untuk mengubah ValueTask menjadi Task. Namun, ValueTask harus digunakan hanya sekali, setelah itu ValueTask harus diabaikan.

Contoh ValueTask di C #

Misalkan Anda memiliki metode asinkron yang mengembalikan Tugas. Anda dapat memanfaatkan Task.FromResult untuk membuat objek Task seperti yang ditunjukkan dalam potongan kode yang diberikan di bawah ini.

Tugas publik GetCustomerIdAsync ()

{

    kembali Task.FromResult (1);

}

Cuplikan kode di atas tidak membuat seluruh sihir mesin status asinkron tetapi mengalokasikan objek Tugas di heap terkelola. Untuk menghindari alokasi ini, Anda mungkin ingin memanfaatkan ValueTask seperti yang ditunjukkan dalam cuplikan kode yang diberikan di bawah ini.

publik ValueTask GetCustomerIdAsync ()

{

    mengembalikan ValueTask baru (1);

}

Cuplikan kode berikut mengilustrasikan implementasi sinkron dari ValueTask.

 public interface IRepository

    {

        ValueTask GetData ();

    }

Kelas Repositori memperluas antarmuka IRepository dan mengimplementasikan metodenya seperti yang ditunjukkan di bawah ini.

    public class Tempat penyimpanan: IRepository

    {

        publik ValueTask GetData ()

        {

            nilai var = default (T);

            mengembalikan ValueTask baru (nilai);

        }

    }

Berikut adalah bagaimana Anda dapat memanggil metode GetData dari metode Utama.

static void Main (string [] args)

        {

            IRepository repository = new Repository ();

            var result = repository.GetData ();

            if (result.IsCompleted)

                 Console.WriteLine ("Operasi selesai ...");

            lain

                Console.WriteLine ("Operasi tidak lengkap ...");

            Console.ReadKey ();

        }

Sekarang mari tambahkan metode lain ke repositori kita, kali ini metode asinkron bernama GetDataAsync. Berikut adalah tampilan antarmuka IRepository yang dimodifikasi.

public interface IRepository

    {

        ValueTask GetData ();

        ValueTask GetDataAsync ();

    }

Metode GetDataAsync diimplementasikan oleh kelas Repositori seperti yang ditunjukkan dalam cuplikan kode yang diberikan di bawah ini.

    public class Tempat penyimpanan: IRepository

    {

        publik ValueTask GetData ()

        {

            nilai var = default (T);

            mengembalikan ValueTask baru (nilai);

        }

        public async ValueTask GetDataAsync ()

        {

            nilai var = default (T);

            menunggu Task.Delay (100);

            nilai kembali;

        }

    }

Kapan saya harus menggunakan ValueTask di C #?

Terlepas dari manfaat yang diberikan ValueTask, ada trade-off tertentu untuk menggunakan ValueTask sebagai pengganti Task. ValueTask adalah tipe nilai dengan dua bidang, sedangkan Tugas adalah tipe referensi dengan satu bidang. Oleh karena itu, menggunakan ValueTask berarti bekerja dengan lebih banyak data karena panggilan metode akan mengembalikan dua bidang data sebagai pengganti satu. Selain itu, jika Anda menunggu metode yang mengembalikan ValueTask, mesin status untuk metode asinkron tersebut juga akan lebih besar - karena harus mengakomodasi struct yang berisi dua bidang sebagai pengganti referensi tunggal dalam kasus Tugas.

Lebih lanjut, jika konsumen metode asinkron menggunakan Task.WhenAll atau Task.WhenAny, menggunakan ValueTask sebagai tipe kembalian dalam metode asinkron mungkin menjadi mahal. Ini karena Anda perlu mengonversi ValueTask menjadi Tugas menggunakan metode AsTask, yang akan menimbulkan alokasi yang dapat dengan mudah dihindari jika Tugas yang di-cache telah digunakan sejak awal.

Inilah aturan dasarnya. Gunakan Tugas jika Anda memiliki potongan kode yang akan selalu asinkron, yaitu saat operasi tidak segera selesai. Manfaatkan ValueTask saat hasil operasi asinkron sudah tersedia atau saat Anda sudah memiliki hasil yang disimpan dalam cache. Apa pun itu, Anda harus melakukan analisis kinerja yang diperlukan sebelum mempertimbangkan ValueTask.

Bagaimana melakukan lebih banyak di C #:

  • Cara menggunakan kekekalan di C
  • Cara menggunakan const, readonly, dan static di C #
  • Cara menggunakan anotasi data di C #
  • Cara bekerja dengan GUID di C # 8
  • Kapan menggunakan kelas abstrak vs. antarmuka di C #
  • Bagaimana bekerja dengan AutoMapper di C #
  • Cara menggunakan ekspresi lambda di C #
  • Cara bekerja dengan delegasi Action, Func, dan Predicate di C #
  • Bagaimana bekerja dengan delegasi di C #
  • Bagaimana menerapkan logger sederhana di C #
  • Bagaimana bekerja dengan atribut di C #
  • Bagaimana bekerja dengan log4net di C #
  • Bagaimana menerapkan pola desain repositori di C #
  • Bagaimana bekerja dengan refleksi di C #
  • Bagaimana bekerja dengan filesystemwatcher di C #
  • Bagaimana melakukan inisialisasi malas di C #
  • Bagaimana bekerja dengan MSMQ di C #
  • Bagaimana bekerja dengan metode ekstensi di C #
  • Bagaimana kami ekspresi lambda di C #
  • Kapan menggunakan kata kunci volatile di C #
  • Cara menggunakan kata kunci hasil di C #
  • Bagaimana menerapkan polimorfisme di C #
  • Cara membuat penjadwal tugas Anda sendiri di C #
  • Bagaimana bekerja dengan RabbitMQ di C #
  • Bagaimana bekerja dengan tupel di C #
  • Menjelajahi metode virtual dan abstrak di C #
  • Cara menggunakan Dapper ORM di C #
  • Cara menggunakan pola desain kelas terbang di C #