Görev Tabanlı Zaman Uyumsuz Deseni Uygulama
Görev Tabanlı Zaman Uyumsuz Deseni (TAP) üç yolla uygulayabilirsiniz: Visual Studio'daki C# ve Visual Basic derleyicilerini el ile veya derleyici ve el ile kullanılan yöntemlerin bir bileşimi aracılığıyla kullanabilirsiniz. Aşağıdaki bölümlerde her yöntem ayrıntılı olarak açıklanmıştır. TAP desenini kullanarak hem işlemle ilişkili hem de G/Ç'ye bağlı zaman uyumsuz işlemleri uygulayabilirsiniz. İş Yükleri bölümünde her işlem türü ele alınmaktadır.
TAP yöntemleri oluşturma
Derleyicileri kullanma
.NET Framework 4.5'den başlayarak, (Async
Visual Basic'te) anahtar sözcüğüyle async
ilişkilendirilen tüm yöntemler zaman uyumsuz bir yöntem olarak kabul edilir ve C# ve Visual Basic derleyicileri, TAP kullanarak yöntemi zaman uyumsuz olarak uygulamak için gerekli dönüştürmeleri gerçekleştirir. Zaman uyumsuz bir yöntem veya System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<TResult> nesnesi döndürmelidir. İkincisi için, işlevin gövdesi bir TResult
döndürmelidir ve derleyici bu sonucun sonuçta elde edilen görev nesnesi aracılığıyla kullanılabilir olmasını sağlar. Benzer şekilde, yöntemin gövdesi içinde işlenmeyen tüm özel durumlar çıkış görevine yönlendirilir ve sonuçta elde edilen görevin durumunda bitmesini TaskStatus.Faulted sağlar. Bu kuralın istisnası, bir OperationCanceledException (veya türetilmiş tür) işlenmediğinde ve bu durumda sonuçta elde edilen görevin durumunda sona ermesidir TaskStatus.Canceled .
TAP yöntemlerini el ile oluşturma
Uygulama üzerinde daha iyi denetim için TAP desenini el ile uygulayabilirsiniz. Derleyici, ad alanından kullanıma sunulan System.Threading.Tasks genel yüzey alanına ve ad alanındaki destek türlerine System.Runtime.CompilerServices dayanır. TAP'yi kendiniz uygulamak için bir TaskCompletionSource<TResult> nesne oluşturur, zaman uyumsuz işlemi gerçekleştirirsiniz ve tamamlandığında , SetExceptionveya yöntemini ya da SetCanceledTry
bu yöntemlerden birinin sürümünü çağırırsınızSetResult. Bir TAP yöntemini el ile uyguladığınızda, temsil edilen zaman uyumsuz işlem tamamlandığında sonuçta elde edilen görevi tamamlamanız gerekir. Örneğin:
public static Task<int> ReadTask(this Stream stream, byte[] buffer, int offset, int count, object state)
{
var tcs = new TaskCompletionSource<int>();
stream.BeginRead(buffer, offset, count, ar =>
{
try { tcs.SetResult(stream.EndRead(ar)); }
catch (Exception exc) { tcs.SetException(exc); }
}, state);
return tcs.Task;
}
<Extension()>
Public Function ReadTask(stream As Stream, buffer() As Byte,
offset As Integer, count As Integer,
state As Object) As Task(Of Integer)
Dim tcs As New TaskCompletionSource(Of Integer)()
stream.BeginRead(buffer, offset, count, Sub(ar)
Try
tcs.SetResult(stream.EndRead(ar))
Catch exc As Exception
tcs.SetException(exc)
End Try
End Sub, state)
Return tcs.Task
End Function
Karma yaklaşım
TAP desenini el ile uygulamayı ancak uygulamanın temel mantığını derleyiciye devretmeyi yararlı bulabilirsiniz. Örneğin, derleyici tarafından oluşturulan zaman uyumsuz bir yöntemin dışındaki bağımsız değişkenleri doğrulamak istediğinizde karma yaklaşımı kullanmak isteyebilirsiniz, böylece özel durumlar nesne üzerinden kullanıma sunulmak yerine yöntemin doğrudan çağırıcısına System.Threading.Tasks.Task kaçabilir:
public Task<int> MethodAsync(string input)
{
if (input == null) throw new ArgumentNullException("input");
return MethodAsyncInternal(input);
}
private async Task<int> MethodAsyncInternal(string input)
{
// code that uses await goes here
return value;
}
Public Function MethodAsync(input As String) As Task(Of Integer)
If input Is Nothing Then Throw New ArgumentNullException("input")
Return MethodAsyncInternal(input)
End Function
Private Async Function MethodAsyncInternal(input As String) As Task(Of Integer)
' code that uses await goes here
return value
End Function
Bu tür bir temsilci seçmenin yararlı olduğu bir diğer durum, hızlı yol iyileştirmesi uyguladığınız ve önbelleğe alınmış bir görevi döndürmek istemenizdir.
İş yükleri
TAP yöntemleri olarak hem işlem bağlı hem de G/Ç bağlı zaman uyumsuz işlemleri uygulayabilirsiniz. Ancak, TAP yöntemleri bir kitaplıktan genel olarak kullanıma sunulduğunda, bunlar yalnızca G/Ç ile ilişkili işlemleri içeren iş yükleri için sağlanmalıdır (hesaplama da içerebilir, ancak yalnızca hesaplama olmamalıdır). Bir yöntem tamamen hesaplamaya bağlıysa, yalnızca zaman uyumlu bir uygulama olarak kullanıma sunulmalıdır. Bunu kullanan kod daha sonra, işi başka bir iş parçacığına boşaltmak veya paralellik elde etmek için bu zaman uyumlu yöntemin çağrısını bir göreve sarmalamayı seçebilir. Bir yöntem G/Ç bağlıysa, yalnızca zaman uyumsuz bir uygulama olarak kullanıma sunulmalıdır.
İşlemle ilişkili görevler
System.Threading.Tasks.Task sınıfı, işlem açısından yoğun işlemleri temsil etmek için idealdir. Varsayılan olarak, verimli yürütme sağlamak için sınıfı içindeki ThreadPool özel desteklerden yararlanır ve ayrıca zaman uyumsuz hesaplamaların ne zaman, nerede ve nasıl yürütüleceği üzerinde önemli bir denetim sağlar.
İşlemle ilişkili görevleri aşağıdaki yollarla oluşturabilirsiniz:
.NET Framework 4.5 ve sonraki sürümlerinde (.NET Core ve .NET 5+ dahil), statik Task.Run yöntemi kısayol TaskFactory.StartNewolarak kullanın. İş parçacığı havuzunu hedefleyen bir işlem bağlı görevi kolayca başlatmak için kullanabilirsiniz Run . Bu, işlem bağlı bir görevi başlatmak için tercih edilen mekanizmadır. Doğrudan yalnızca görev üzerinde daha ayrıntılı denetim istediğinizde kullanın
StartNew
..NET Framework 4'te, zaman uyumsuz olarak yürütülecek bir temsilciyi (genellikle bir Action<T> veya bir Func<TResult>) kabul eden yöntemini kullanınTaskFactory.StartNew. Bir Action<T> temsilci sağlarsanız, yöntemi bu temsilcinin zaman uyumsuz yürütmesini temsil eden bir System.Threading.Tasks.Task nesne döndürür. Bir Func<TResult> temsilci sağlarsanız, yöntemi bir System.Threading.Tasks.Task<TResult> nesnesi döndürür. yönteminin StartNew aşırı yüklemeleri bir iptal belirteci (CancellationToken), görev oluşturma seçenekleri ()TaskCreationOptions ve tümü görevin zamanlanması ve yürütülmesi üzerinde ayrıntılı denetim sağlayan bir görev zamanlayıcıyı (TaskScheduler ) kabul eder. Geçerli görev zamanlayıcısını hedefleyen bir fabrika örneği, sınıfın Task statik özelliği (Factory) olarak kullanılabilir; örneğin:
Task.Factory.StartNew(…)
.Görevi ayrı olarak oluşturmak ve
Start
zamanlamak istiyorsanız türü ve yönteminin oluşturucularınıTask
kullanın. Genel yöntemler yalnızca önceden başlatılmış görevleri döndürmelidir.yönteminin Task.ContinueWith aşırı yüklemelerini kullanın. Bu yöntem, başka bir görev tamamlandığında zamanlanan yeni bir görev oluşturur. Aşırı yüklemelerden bazıları, devamlılık görevinin ContinueWith zamanlanması ve yürütülmesi üzerinde daha iyi denetim için bir iptal belirteci, devamlılık seçenekleri ve bir görev zamanlayıcı kabul eder.
TaskFactory.ContinueWhenAll ve TaskFactory.ContinueWhenAny yöntemlerini kullanın. Bu yöntemler, sağlanan bir görev kümesinin tümü veya herhangi biri tamamlandığında zamanlanan yeni bir görev oluşturur. Bu yöntemler ayrıca bu görevlerin zamanlamasını ve yürütülmesini denetlemek için aşırı yüklemeler sağlar.
İşlem bağlantılı görevlerde sistem, görevi çalıştırmaya başlamadan önce bir iptal isteği alırsa zamanlanmış bir görevin yürütülmesini engelleyebilir. Bu nedenle, bir iptal belirteci (CancellationToken nesnesi) sağlarsanız, belirteci belirteci izleyen zaman uyumsuz koda geçirebilirsiniz. Ayrıca, çalışma zamanının belirteci de izleyebilmesi için veya gibi StartNew
Run
Task
daha önce bahsedilen yöntemlerden birine belirteci sağlayabilirsiniz.
Örneğin, bir görüntüyü işleyen zaman uyumsuz bir yöntem düşünün. görevin gövdesi, işleme sırasında bir iptal isteği geldiğinde kodun erken çıkabilmesi için iptal belirtecini yoklayabilir. Ayrıca, işleme başlamadan önce iptal isteği gelirse işleme işlemini engellemek istersiniz:
internal Task<Bitmap> RenderAsync(
ImageData data, CancellationToken cancellationToken)
{
return Task.Run(() =>
{
var bmp = new Bitmap(data.Width, data.Height);
for(int y=0; y<data.Height; y++)
{
cancellationToken.ThrowIfCancellationRequested();
for(int x=0; x<data.Width; x++)
{
// render pixel [x,y] into bmp
}
}
return bmp;
}, cancellationToken);
}
Friend Function RenderAsync(data As ImageData, cancellationToken As _
CancellationToken) As Task(Of Bitmap)
Return Task.Run(Function()
Dim bmp As New Bitmap(data.Width, data.Height)
For y As Integer = 0 to data.Height - 1
cancellationToken.ThrowIfCancellationRequested()
For x As Integer = 0 To data.Width - 1
' render pixel [x,y] into bmp
Next
Next
Return bmp
End Function, cancellationToken)
End Function
Aşağıdaki koşullardan en az biri doğruysa, işlem bağlantılı görevler bir durumda biter Canceled :
İptal isteği, görev duruma geçmeden Running önce oluşturma yöntemine (örneğin veya
Run
StartNew
) bağımsız değişken olarak sağlanan nesnesi aracılığıyla CancellationToken ulaşır.Böyle OperationCanceledException bir görevin gövdesinde bir özel durum işlenmemiştir, bu özel durum göreve geçirilenin aynısını CancellationToken içerir ve bu belirteç iptalin istendiğini gösterir.
Görevin gövdesinde başka bir özel durum işlenmemişse, görev durumunda sona erer Faulted ve görevde beklemeye veya sonucuna erişmeye yönelik tüm girişimler bir özel durum oluşturmasına neden olur.
G/Ç ilişkili görevler
Bir iş parçacığı tarafından yürütülmesinin tamamı için doğrudan yedeklenmemesi gereken bir görev oluşturmak için türünü kullanın TaskCompletionSource<TResult> . Bu tür, ilişkili Task<TResult> bir Task örneği döndüren bir özelliği kullanıma sunar. Bu görevin yaşam döngüsü, , SetException, SetCanceledve bunların varyantları TrySet
gibi SetResultyöntemler tarafından TaskCompletionSource<TResult> denetlenir.
Belirtilen süre sonunda tamamlanacak bir görev oluşturmak istediğinizi varsayalım. Örneğin, kullanıcı arabirimindeki bir etkinliği geciktirmek isteyebilirsiniz. System.Threading.Timer sınıfı zaten belirli bir süre sonra zaman uyumsuz olarak bir temsilci çağırma olanağı sağlar ve kullanarak TaskCompletionSource<TResult> zamanlayıcıya bir Task<TResult> ön koyabilirsiniz, örneğin:
public static Task<DateTimeOffset> Delay(int millisecondsTimeout)
{
TaskCompletionSource<DateTimeOffset> tcs = null;
Timer timer = null;
timer = new Timer(delegate
{
timer.Dispose();
tcs.TrySetResult(DateTimeOffset.UtcNow);
}, null, Timeout.Infinite, Timeout.Infinite);
tcs = new TaskCompletionSource<DateTimeOffset>(timer);
timer.Change(millisecondsTimeout, Timeout.Infinite);
return tcs.Task;
}
Public Function Delay(millisecondsTimeout As Integer) As Task(Of DateTimeOffset)
Dim tcs As TaskCompletionSource(Of DateTimeOffset) = Nothing
Dim timer As Timer = Nothing
timer = New Timer(Sub(obj)
timer.Dispose()
tcs.TrySetResult(DateTimeOffset.UtcNow)
End Sub, Nothing, Timeout.Infinite, Timeout.Infinite)
tcs = New TaskCompletionSource(Of DateTimeOffset)(timer)
timer.Change(millisecondsTimeout, Timeout.Infinite)
Return tcs.Task
End Function
Task.Delay yöntemi bu amaçla sağlanır ve bunu başka bir zaman uyumsuz yöntem içinde kullanabilirsiniz, örneğin, zaman uyumsuz yoklama döngüsü uygulamak için:
public static async Task Poll(Uri url, CancellationToken cancellationToken,
IProgress<bool> progress)
{
while(true)
{
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
bool success = false;
try
{
await DownloadStringAsync(url);
success = true;
}
catch { /* ignore errors */ }
progress.Report(success);
}
}
Public Async Function Poll(url As Uri, cancellationToken As CancellationToken,
progress As IProgress(Of Boolean)) As Task
Do While True
Await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken)
Dim success As Boolean = False
Try
await DownloadStringAsync(url)
success = true
Catch
' ignore errors
End Try
progress.Report(success)
Loop
End Function
Sınıfının TaskCompletionSource<TResult> genel olmayan bir karşılığı yoktur. Ancak, Task<TResult> öğesinden Tasktüretilir, böylece yalnızca bir görev döndüren G/Ç ile ilişkili yöntemler için genel TaskCompletionSource<TResult> nesneyi kullanabilirsiniz. Bunu yapmak için, manken TResult
içeren bir kaynak kullanabilirsiniz (Boolean iyi bir varsayılan seçenektir, ancak bunu bir 'e Task<TResult>indiren kullanıcıyla Task ilgileniyorsanız, bunun yerine özel TResult
bir tür kullanabilirsiniz). Örneğin, Delay
önceki örnekteki yöntem, elde edilen uzaklık (Task<DateTimeOffset>
) ile birlikte geçerli saati döndürür. Böyle bir sonuç değeri gereksizse, yöntemi bunun yerine aşağıdaki gibi kodlanabilir (dönüş türü değişikliğine ve bağımsız değişkenin olarak değiştirilmesine TrySetResultdikkat edin):
public static Task<bool> Delay(int millisecondsTimeout)
{
TaskCompletionSource<bool> tcs = null;
Timer timer = null;
timer = new Timer(delegate
{
timer.Dispose();
tcs.TrySetResult(true);
}, null, Timeout.Infinite, Timeout.Infinite);
tcs = new TaskCompletionSource<bool>(timer);
timer.Change(millisecondsTimeout, Timeout.Infinite);
return tcs.Task;
}
Public Function Delay(millisecondsTimeout As Integer) As Task(Of Boolean)
Dim tcs As TaskCompletionSource(Of Boolean) = Nothing
Dim timer As Timer = Nothing
Timer = new Timer(Sub(obj)
timer.Dispose()
tcs.TrySetResult(True)
End Sub, Nothing, Timeout.Infinite, Timeout.Infinite)
tcs = New TaskCompletionSource(Of Boolean)(timer)
timer.Change(millisecondsTimeout, Timeout.Infinite)
Return tcs.Task
End Function
İşlemle ilişkili ve G/Ç ile ilişkili karma görevler
Zaman uyumsuz yöntemler yalnızca işlemle veya G/Ç ile ilişkili işlemlerle sınırlı değildir, aynı zamanda ikisinin bir karışımını temsil edebilir. Aslında, birden çok zaman uyumsuz işlem genellikle daha büyük karma işlemler halinde birleştirilir. Örneğin, önceki bir örnekte gösterilen RenderAsync
yöntemi, bazı giriş imageData
verilerine göre bir resmi işlemek için yoğun bir hesaplama işlem gerçekleştirdi. Bu imageData
, zaman uyumsuz olarak erişdiğiniz bir web hizmetinden gelebilir:
public async Task<Bitmap> DownloadDataAndRenderImageAsync(
CancellationToken cancellationToken)
{
var imageData = await DownloadImageDataAsync(cancellationToken);
return await RenderAsync(imageData, cancellationToken);
}
Public Async Function DownloadDataAndRenderImageAsync(
cancellationToken As CancellationToken) As Task(Of Bitmap)
Dim imageData As ImageData = Await DownloadImageDataAsync(cancellationToken)
Return Await RenderAsync(imageData, cancellationToken)
End Function
Bu örnekte, tek bir iptal belirtecinin birden çok zaman uyumsuz işlem aracılığıyla nasıl iş parçacıklı olabileceği de gösterilmektedir. Daha fazla bilgi için Görev Tabanlı Zaman Uyumsuz Deseni Kullanma bölümündeki iptal kullanımı bölümüne bakın.