Interop with Other Asynchronous Patterns and Types
A .NET aszinkron mintáinak rövid története:
- .NET-keretrendszer 1.0 bevezette a IAsyncResult mintát, más néven aszinkron programozási modellt (APM) vagy a
Begin/End
mintát. - .NET-keretrendszer 2.0 hozzáadta a Eseményalapú aszinkron minta (EAP).
- .NET-keretrendszer 4 bevezette a Feladatalapú aszinkron minta (TAP), amely felváltja az APM-t és az EAP-t is, és lehetővé teszi a migrálási rutinok egyszerű összeállítását a korábbi mintákból.
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
. DownloadStringAsync
elfogadja 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
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: