Aracılığıyla paylaş


Diğer Zaman Uyumsuz Desen ve Türlerle Birlikte Çalışma

.NET'te zaman uyumsuz desenlerin kısa geçmişi:

  • .NET Framework 1.0, zaman uyumsuz programlama modeli (APM) veya desen olarak da bilinen deseni kullanıma sunmşu IAsyncResultBegin/End.
  • .NET Framework 2.0, Olay Tabanlı Zaman Uyumsuz Deseni (EAP) ekledi.
  • .NET Framework 4, hem APM hem de EAP'nin yerini alan ve önceki desenlerden kolayca geçiş yordamları oluşturma olanağı sağlayan Görev Tabanlı Zaman Uyumsuz Deseni (TAP) kullanıma sunulmuştur.

Görevler ve Zaman Uyumsuz Programlama Modeli (APM)

APM'den TAP'ye

Zaman Uyumsuz Programlama Modeli (APM) deseni yapılandırılmış olduğundan, APM uygulamasını TAP uygulaması olarak kullanıma sunma amacıyla sarmalayıcı oluşturmak oldukça kolaydır. .NET Framework 4 ve sonraki sürümleri, bu çeviriyi sağlamak için yöntem aşırı yüklemeleri biçiminde FromAsync yardımcı yordamlar içerir.

Zaman uyumlu Read yöntemine Stream karşılık gelen APM'yi temsil eden sınıfını ve ve BeginReadEndRead yöntemlerini göz önünde bulundurun:

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

Bu işlem için bir TAP sarmalayıcı uygulamak için yöntemini aşağıdaki gibi kullanabilirsiniz TaskFactory<TResult>.FromAsync :

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

Bu uygulama aşağıdakine benzer:

 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

TAP'den APM'ye

Mevcut altyapınız APM desenini bekliyorsa, bir TAP uygulaması alıp bunu bir APM uygulamasının beklendiği yerde de kullanmak istersiniz. Görevler oluşturulabildikleri için ve Task sınıfı IAsyncResult arabirimini uyguladığı için, bunu yapmak amacıyla daha basit bir yardımcı işlev kullanabilirsiniz. Aşağıdaki kod sınıfın bir uzantısını Task<TResult> kullanır, ancak genel olmayan görevler için neredeyse aynı işlevi kullanabilirsiniz.

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

Şimdi aşağıdaki TAP uygulamasına sahip olduğunuz bir olayı göz önünde bulundurun:

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

ve bu APM uygulamasını sağlamak istiyorsunuz:

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

Aşağıdaki örnekte APM'ye bir geçiş gösterilmektedir:

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

Görevler ve Olay Tabanlı Zaman Uyumsuz Desen (EAP)

Olay Tabanlı Zaman Uyumsuz Desen (EAP) uygulamasını sarmalama, APM desenini sarmalamaktan daha önemlidir, çünkü EAP deseni APM deseninden daha fazla varyasyona ve daha az yapıya sahiptir. Göstermek için aşağıdaki kod yöntemini sarmalar DownloadStringAsync . DownloadStringAsync bir URI kabul eder, DownloadProgressChanged ilerleme durumuyla ilgili birden çok istatistiği raporlamak için indirme sırasında olayı tetikler ve tamamlandığında olayı tetikler DownloadStringCompleted . Sonuç, belirtilen URI'deki sayfanın içeriğini içeren bir dizedir.

 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

Görevler ve Bekleme Tutamaçları

Bekleme Tutamaçlarından TAP'a

Bekleme tutamaçları zaman uyumsuz bir desen uygulamasa da, gelişmiş geliştiriciler bir bekleme tutamacı ayarlandığında zaman uyumsuz bildirimler için sınıfını ve ThreadPool.RegisterWaitForSingleObject yöntemini kullanabilirWaitHandle. Bir bekleme tutamacı üzerindeki RegisterWaitForSingleObject zaman uyumlu beklemelere görev tabanlı bir alternatifi etkinleştirmek için yöntemini sarmalayabilirsiniz:

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

Bu yöntemle, mevcut WaitHandle uygulamaları zaman uyumsuz yöntemlerde kullanabilirsiniz. Örneğin, belirli bir zamanda yürütülen zaman uyumsuz işlemlerin sayısını kısıtlamak istiyorsanız, semafor ( System.Threading.SemaphoreSlim nesne) kullanabilirsiniz. Semaforun sayısını N olarak başlatarak, bir işlemi gerçekleştirmek istediğiniz zaman semaforu bekleyerek ve işlemi tamamladığınızda semaforu serbest bırakarak eşzamanlı olarak çalışan işlem sayısını N olarak kısıtlayabilirsiniz:

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

Ayrıca bekleme tutamaçlarını kullanmayan ve bunun yerine görevlerle tamamen çalışan zaman uyumsuz bir semafor da oluşturabilirsiniz. Bunu yapmak için, üzerinde Taskveri yapıları oluşturmak için Görev Tabanlı Zaman Uyumsuz Deseni Kullanma bölümünde açıklanan teknikler gibi teknikleri kullanabilirsiniz.

TAP'tan Bekleme Tutamaçlarına

Daha önce belirtildiği gibi sınıfı Task uygular IAsyncResultve bu uygulama tamamlandığında ayarlanacak Task bir bekleme tutamacı döndüren bir IAsyncResult.AsyncWaitHandle özelliği kullanıma sunar. için aşağıdaki gibi bir WaitHandleTask alabilirsiniz:

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

Ayrıca bkz.