Share via


Interop dengan Pola dan Jenis Asinkron Lainnya

Sejarah singkat pola asinkron dalam .NET:

Tugas dan Model Pemrograman Asinkron (APM)

Dari APM ke TAP

Karena pola Asynchronous Programming Model (APM) terstruktur, cukup mudah untuk membangun pembungkus untuk mengekspos implementasi APM sebagai implementasi TAP. .NET Framework 4 dan versi yang lebih baru termasuk rutinitas pembantu dalam bentuk FromAsync metode kelebihan beban untuk memberikan terjemahan ini.

Pertimbangkan Stream kelas BeginRead dan EndRead metodenya, yang mewakili pasangan APM ke metode sinkronRead:

public int Read(byte[] buffer, int offset, int count)
Public Function Read(buffer As Byte(), offset As Integer,
                     count As Integer) As Integer
public IAsyncResult BeginRead(byte[] buffer, int offset,
                              int count, AsyncCallback callback,
                              object state)
Public Function BeginRead(buffer As Byte, offset As Integer,
                          count As Integer, callback As AsyncCallback,
                          state As Object) As IAsyncResult
public int EndRead(IAsyncResult asyncResult)
Public Function EndRead(asyncResult As IAsyncResult) As Integer

Anda dapat menggunakan TaskFactory<TResult>.FromAsync metode untuk mengimplementasikan pembungkus TAP untuk operasi ini sebagai berikut:

public static Task<int> ReadAsync(this Stream stream,
                                  byte[] buffer, int offset,
                                  int count)
{
    if (stream == null)
       throw new ArgumentNullException("stream");

    return Task<int>.Factory.FromAsync(stream.BeginRead,
                                       stream.EndRead, buffer,
                                       offset, count, null);
}
<Extension()>
Public Function ReadAsync(strm As Stream,
                          buffer As Byte(), offset As Integer,
                          count As Integer) As Task(Of Integer)
    If strm Is Nothing Then
        Throw New ArgumentNullException("stream")
    End If

    Return Task(Of Integer).Factory.FromAsync(AddressOf strm.BeginRead,
                                              AddressOf strm.EndRead, buffer,
                                              offset, count, Nothing)
End Function

Implementasi ini mirip dengan yang berikut:

 public static Task<int> ReadAsync(this Stream stream,
                                   byte [] buffer, int offset,
                                   int count)
 {
    if (stream == null)
        throw new ArgumentNullException("stream");

    var tcs = new TaskCompletionSource<int>();
    stream.BeginRead(buffer, offset, count, iar =>
                     {
                        try {
                           tcs.TrySetResult(stream.EndRead(iar));
                        }
                        catch(OperationCanceledException) {
                           tcs.TrySetCanceled();
                        }
                        catch(Exception exc) {
                           tcs.TrySetException(exc);
                        }
                     }, null);
    return tcs.Task;
}

<Extension()>
Public Function ReadAsync(stream As Stream, buffer As Byte(), _
                          offset As Integer, count As Integer) _
                          As Task(Of Integer)
    If stream Is Nothing Then
        Throw New ArgumentNullException("stream")
    End If

    Dim tcs As New TaskCompletionSource(Of Integer)()
    stream.BeginRead(buffer, offset, count,
                     Sub(iar)
                         Try
                             tcs.TrySetResult(stream.EndRead(iar))
                         Catch e As OperationCanceledException
                             tcs.TrySetCanceled()
                         Catch e As Exception
                             tcs.TrySetException(e)
                         End Try
                     End Sub, Nothing)
    Return tcs.Task
End Function

Dari TAP ke APM

Jika infrastruktur yang ada mengharapkan pola APM, Anda juga ingin mengambil implementasi TAP dan menggunakannya di tempat implementasi APM diharapkan. Karena tugas dapat disusun dan Task kelas mengimplementasikan IAsyncResult, Anda dapat menggunakan fungsi pembantu yang mudah untuk melakukan ini. Kode berikut menggunakan ekstensi Task<TResult> kelas, tetapi Anda dapat menggunakan fungsi yang hampir identik untuk tugas nongenerik.

public static IAsyncResult AsApm<T>(this Task<T> task,
                                    AsyncCallback callback,
                                    object state)
{
    if (task == null)
        throw new ArgumentNullException("task");

    var tcs = new TaskCompletionSource<T>(state);
    task.ContinueWith(t =>
                      {
                         if (t.IsFaulted)
                            tcs.TrySetException(t.Exception.InnerExceptions);
                         else if (t.IsCanceled)
                            tcs.TrySetCanceled();
                         else
                            tcs.TrySetResult(t.Result);

                         if (callback != null)
                            callback(tcs.Task);
                      }, TaskScheduler.Default);
    return tcs.Task;
}
<Extension()>
Public Function AsApm(Of T)(task As Task(Of T),
                            callback As AsyncCallback,
                            state As Object) As IAsyncResult
    If task Is Nothing Then
        Throw New ArgumentNullException("task")
    End If

    Dim tcs As New TaskCompletionSource(Of T)(state)
    task.ContinueWith(Sub(antecedent)
                          If antecedent.IsFaulted Then
                              tcs.TrySetException(antecedent.Exception.InnerExceptions)
                          ElseIf antecedent.IsCanceled Then
                              tcs.TrySetCanceled()
                          Else
                              tcs.TrySetResult(antecedent.Result)
                          End If

                          If callback IsNot Nothing Then
                              callback(tcs.Task)
                          End If
                      End Sub, TaskScheduler.Default)
    Return tcs.Task
End Function

Sekarang, pertimbangkan kasus ketika Anda memiliki implementasi TAP berikut:

public static Task<String> DownloadStringAsync(Uri url)
Public Shared Function DownloadStringAsync(url As Uri) As Task(Of String)

dan Anda ingin memberikan implementasi APM ini:

public IAsyncResult BeginDownloadString(Uri url,
                                        AsyncCallback callback,
                                        object state)
Public Function BeginDownloadString(url As Uri,
                                    callback As AsyncCallback,
                                    state As Object) As IAsyncResult
public string EndDownloadString(IAsyncResult asyncResult)
Public Function EndDownloadString(asyncResult As IAsyncResult) As String

Contoh berikut menunjukkan satu migrasi ke APM:

public IAsyncResult BeginDownloadString(Uri url,
                                        AsyncCallback callback,
                                        object state)
{
   return DownloadStringAsync(url).AsApm(callback, state);
}

public string EndDownloadString(IAsyncResult asyncResult)
{
   return ((Task<string>)asyncResult).Result;
}
Public Function BeginDownloadString(url As Uri,
                                    callback As AsyncCallback,
                                    state As Object) As IAsyncResult
    Return DownloadStringAsync(url).AsApm(callback, state)
End Function

Public Function EndDownloadString(asyncResult As IAsyncResult) As String
    Return CType(asyncResult, Task(Of String)).Result
End Function

Tugas dan Pola Asinkron Berbasis Peristiwa (EAP)

Membungkus implementasi Pola Asinkron Berbasis Peristiwa (EAP) lebih terlibat daripada membungkus pola APM, karena pola EAP memiliki lebih banyak variasi dan struktur yang lebih sedikit daripada pola APM. Untuk menunjukkan, kode berikut membungkus DownloadStringAsync metode. DownloadStringAsync menerima URI, meningkatkan DownloadProgressChanged peristiwa saat mengunduh untuk melaporkan beberapa statistik yang sedang berlangsung, dan meningkatkan DownloadStringCompleted peristiwa saat selesai. Hasil akhirnya adalah string yang berisi isi halaman pada URI yang ditentukan.

 public static Task<string> DownloadStringAsync(Uri url)
 {
     var tcs = new TaskCompletionSource<string>();
     var wc = new WebClient();
     wc.DownloadStringCompleted += (s,e) =>
         {
             if (e.Error != null)
                tcs.TrySetException(e.Error);
             else if (e.Cancelled)
                tcs.TrySetCanceled();
             else
                tcs.TrySetResult(e.Result);
         };
     wc.DownloadStringAsync(url);
     return tcs.Task;
}
Public Shared Function DownloadStringAsync(url As Uri) As Task(Of String)
    Dim tcs As New TaskCompletionSource(Of String)()
    Dim wc As New WebClient()
    AddHandler wc.DownloadStringCompleted, Sub(s, e)
                                               If e.Error IsNot Nothing Then
                                                   tcs.TrySetException(e.Error)
                                               ElseIf e.Cancelled Then
                                                   tcs.TrySetCanceled()
                                               Else
                                                   tcs.TrySetResult(e.Result)
                                               End If
                                           End Sub
    wc.DownloadStringAsync(url)
    Return tcs.Task
End Function

Tugas dan Pegangan Tunggu

Dari Pegangan Tunggu ke TAP

Meskipun handel tunggu tidak menerapkan pola asinkron, pengembang tingkat lanjut dapat menggunakan WaitHandle kelas dan ThreadPool.RegisterWaitForSingleObject metode untuk pemberitahuan asinkron saat handel tunggu diatur. Anda dapat membungkus RegisterWaitForSingleObject metode untuk mengaktifkan alternatif berbasis tugas untuk setiap tunggu sinkron pada handel tunggu:

public static Task WaitOneAsync(this WaitHandle waitHandle)
{
    if (waitHandle == null)
        throw new ArgumentNullException("waitHandle");

    var tcs = new TaskCompletionSource<bool>();
    var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle,
        delegate { tcs.TrySetResult(true); }, null, -1, true);
    var t = tcs.Task;
    t.ContinueWith( (antecedent) => rwh.Unregister(null));
    return t;
}
<Extension()>
Public Function WaitOneAsync(waitHandle As WaitHandle) As Task
    If waitHandle Is Nothing Then
        Throw New ArgumentNullException("waitHandle")
    End If

    Dim tcs As New TaskCompletionSource(Of Boolean)()
    Dim rwh As RegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(waitHandle,
        Sub(state, timedOut)
            tcs.TrySetResult(True)
        End Sub, Nothing, -1, True)
    Dim t = tcs.Task
    t.ContinueWith(Sub(antecedent)
                       rwh.Unregister(Nothing)
                   End Sub)
    Return t
End Function

Dengan metode ini, Anda dapat menggunakan implementasi yang ada WaitHandle dalam metode asinkron. Misalnya, jika Anda ingin membatasi jumlah operasi asinkron yang dijalankan pada waktu tertentu, Anda dapat menggunakan semaphore ( System.Threading.SemaphoreSlim objek). Anda dapat membatasi ke N jumlah operasi yang berjalan bersamaan dengan menginisialisasi jumlah semaphore ke N, menunggu semaphore kapan saja Anda ingin melakukan operasi, dan melepaskan semaphore ketika Anda selesai dengan operasi:

static int N = 3;

static SemaphoreSlim m_throttle = new SemaphoreSlim(N, N);

static async Task DoOperation()
{
    await m_throttle.WaitAsync();
    // do work
    m_throttle.Release();
}
Shared N As Integer = 3

Shared m_throttle As New SemaphoreSlim(N, N)

Shared Async Function DoOperation() As Task
    Await m_throttle.WaitAsync()
    ' Do work.
    m_throttle.Release()
End Function

Anda juga dapat membangun semaphore asinkron yang tidak bergantung pada pegangan tunggu dan sebaliknya bekerja sepenuhnya dengan tugas. Untuk melakukan ini, Anda dapat menggunakan teknik seperti yang dibahas dalam Mengonsumsi Pola Asinkron Berbasis Tugas untuk membangun struktur data di atas Task.

Dari TAP ke Wait Handle

Seperti yang disebutkan sebelumnya, Task kelas mengimplementasikan IAsyncResult, dan implementasi tersebut mengekspos IAsyncResult.AsyncWaitHandle properti yang mengembalikan handel tunggu yang akan diatur ketika Task selesai. Anda bisa mendapatkan WaitHandle untuk sebagai Task berikut:

WaitHandle wh = ((IAsyncResult)task).AsyncWaitHandle;
Dim wh As WaitHandle = CType(task, IAsyncResult).AsyncWaitHandle

Lihat juga