Jika Anda tidak ingin menggunakan async / menunggu di dalam metode Anda, tetapi masih "hiasi" itu agar dapat menggunakan kata kunci tunggu dari luar, TaskCompletionSource.cs :
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
Dari sini dan sini
Untuk mendukung paradigma seperti itu dengan Tugas, kita perlu cara untuk mempertahankan façade Tugas dan kemampuan untuk merujuk pada operasi asinkron yang sewenang-wenang sebagai suatu Tugas, tetapi untuk mengontrol masa kerja Tugas tersebut sesuai dengan aturan infrastruktur dasar yang menyediakan sinkronisasi, dan untuk melakukannya dengan cara yang tidak memerlukan biaya yang signifikan. Ini adalah tujuan dari TaskCompletionSource.
Saya melihat juga digunakan dalam sumber NET misalnya. WebClient.cs :
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<string>(address);
// Setup the callback event handler
UploadStringCompletedEventHandler handler = null;
handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
this.UploadStringCompleted += handler;
// Start the async operation.
try { this.UploadStringAsync(address, method, data, tcs); }
catch
{
this.UploadStringCompleted -= handler;
throw;
}
// Return the task that represents the async operation
return tcs.Task;
}
Akhirnya, saya menemukan berguna juga sebagai berikut:
Saya selalu ditanya pertanyaan ini. Implikasinya adalah bahwa harus ada beberapa utas di suatu tempat yang memblokir panggilan I / O ke sumber daya eksternal. Jadi, kode asinkron membebaskan utas permintaan, tetapi hanya dengan mengorbankan utas lain di sistem, kan? Tidak, tidak sama sekali. Untuk memahami mengapa skala permintaan asinkron, saya akan melacak (disederhanakan) contoh panggilan I / O asinkron. Katakanlah permintaan perlu menulis ke file. Utas permintaan memanggil metode penulisan asinkron. WriteAsync diimplementasikan oleh Base Class Library (BCL), dan menggunakan port penyelesaian untuk I / O yang tidak sinkron. Jadi, panggilan WriteAsync diturunkan ke OS sebagai file asynchronous tulis. OS kemudian berkomunikasi dengan tumpukan driver, meneruskan data untuk menulis dalam paket permintaan I / O (IRP). Di sinilah hal-hal menjadi menarik: Jika driver perangkat tidak dapat menangani IRP segera, ia harus menanganinya secara tidak sinkron. Jadi, pengemudi memberi tahu disk untuk mulai menulis dan mengembalikan respons "pending" ke OS. OS meneruskan respons "pending" ke BCL, dan BCL mengembalikan tugas yang tidak lengkap ke kode penanganan permintaan. Kode penanganan permintaan menunggu tugas, yang mengembalikan tugas tidak lengkap dari metode itu dan seterusnya. Akhirnya, kode penanganan permintaan akhirnya mengembalikan tugas yang tidak lengkap ke ASP.NET, dan utas permintaan dibebaskan untuk kembali ke kumpulan utas. Kode penanganan permintaan menunggu tugas, yang mengembalikan tugas tidak lengkap dari metode itu dan seterusnya. Akhirnya, kode penanganan permintaan akhirnya mengembalikan tugas yang tidak lengkap ke ASP.NET, dan utas permintaan dibebaskan untuk kembali ke kumpulan utas. Kode penanganan permintaan menunggu tugas, yang mengembalikan tugas tidak lengkap dari metode itu dan seterusnya. Akhirnya, kode penanganan permintaan akhirnya mengembalikan tugas yang tidak lengkap ke ASP.NET, dan utas permintaan dibebaskan untuk kembali ke kumpulan utas.
Pengantar Async / Menunggu di ASP.NET
Jika targetnya adalah untuk meningkatkan skalabilitas (bukan responsif), semuanya bergantung pada keberadaan I / O eksternal yang memberikan peluang untuk melakukan itu.