Megosztás a következőn keresztül:


Interop with Other Asynchronous Patterns and Types

A .NET aszinkron mintáinak rövid története:

Feladatok és aszinkron programozási modell (APM)

Az APM-ről a TAP-ra

Mivel az aszinkron programozási modell (APM) mintája strukturált, elég könnyű burkolót létrehozni, hogy az APM-implementációt TAP-implementációként tegye elérhetővé. .NET-keretrendszer 4- és újabb verziókban a segéd rutinok metódustúlterheltség FromAsync formájában jelenik meg a fordítás biztosításához.

Vegye figyelembe az Stream osztályt és annak BeginRead és EndRead módszereit, amelyek a szinkron metódus APM-megfelelőjét képviselik Read :

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

A metódussal TaskFactory<TResult>.FromAsync az alábbiak szerint implementálhat EGY TAP burkolót ehhez a művelethez:

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

Ez a megvalósítás a következőhöz hasonló:

 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

A TAP-tól az APM-ig

Ha a meglévő infrastruktúra elvárja az APM-mintát, akkor a TAP-implementációt is érdemes használnia, ahol az APM-implementáció várható. Mivel a feladatok összeállíthatók és az Task osztály implementálható IAsyncResult, ehhez használhat egy egyszerű segédfüggvényt. Az alábbi kód az Task<TResult> osztály egy bővítményét használja, de szinte azonos függvényt használhat nem általános feladatokhoz.

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

Most fontolja meg azt az esetet, amikor a következő TAP-implementációval rendelkezik:

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

és meg szeretné adni ezt az APM-implementációt:

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

Az alábbi példa egy áttelepítést mutat be az APM-be:

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

Feladatok és az eseményalapú aszinkron minta (EAP)

Az eseményalapú aszinkron minta (EAP) implementációjának burkolása nagyobb szerepet játszik, mint egy APM-minta körbefuttatása, mivel az EAP-minta több variációval és kevesebb struktúrával rendelkezik, mint az APM-minta. A szemléltetés érdekében a következő kód körbefuttatja a metódust DownloadStringAsync . DownloadStringAsyncelfogadja az URI-t, letölti az DownloadProgressChanged eseményt, hogy több statisztikát jelentsen az előrehaladásról, és amikor elkészült, az eseményt.DownloadStringCompleted A végeredmény egy sztring, amely a lap tartalmát tartalmazza a megadott URI-n.

 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

Feladatok és várakozási fogópontok

Várakozási fogópontoktól a TAP-ig

Bár a várakozási fogópontok nem implementálnak aszinkron mintát, a haladó fejlesztők használhatják az WaitHandle osztályt és a ThreadPool.RegisterWaitForSingleObject metódust az aszinkron értesítésekhez a várakozási fogópont beállításakor. A metódust körbefuttatva engedélyezheti a RegisterWaitForSingleObject feladatalapú alternatívát a várakozási fogóponton lévő szinkron várakozással:

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

Ezzel a módszerrel a meglévő WaitHandle implementációkat aszinkron metódusokban használhatja. Ha például egy adott időpontban végrehajtott aszinkron műveletek számát szeretné szabályozni, használhat szemaphore-t (objektumot System.Threading.SemaphoreSlim ). Az egyidejűleg futó műveletek számát N-re szabályozhatja, ha a szemapóra számát N-re inicializálja, a szemapóra vár, amikor egy műveletet végre szeretne hajtani, és felszabadítja a szemaphore-t, amikor végzett egy művelettel:

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

Olyan aszinkron szemaphore is létrehozható, amely nem támaszkodik a várakozási fogópontokra, hanem teljesen együttműködik a feladatokkal. Ehhez használhat olyan technikákat, mint a feladatalapú aszinkron minta használata az adatstruktúrák tetején Task.

A TAP-tól a várakozási fogópontokig

Ahogy korábban említettük, az osztály implementáljaIAsyncResult, Task és az implementáció egy IAsyncResult.AsyncWaitHandle olyan tulajdonságot tesz elérhetővé, amely egy várakozási fogópontot ad vissza, amely a Task befejezéskor lesz beállítva. A következőkhöz Task juthat WaitHandle hozzá:

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

Lásd még