Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
ve asyncile await çalışırken, iki bağlam türü önemli ama çok farklı roller oynar: ExecutionContext ve SynchronizationContext. Her birinin ne yaptığını, her birinin async/await ile nasıl etkileşim kurduğunu ve neden SynchronizationContext.Current bekleme noktaları arasında geçmediğini öğrenirsiniz.
ExecutionContext nedir?
ExecutionContext , programınızın mantıksal denetim akışıyla akan ortam durumu için bir kapsayıcıdır. Zaman uyumlu bir dünyada ortam bilgileri iş parçacığı yerel depolamasında (TLS) bulunur ve belirli bir iş parçacığında çalışan tüm kodlar bu verileri görür. Zaman uyumsuz bir dünyada, mantıksal bir işlem bir iş parçacığında başlatılabilir, askıya alınabilir ve farklı bir iş parçacığında devam ettirilebilir. İş parçacığı yerel verileri otomatik olarak takip etmez, ExecutionContext onu takip etmesini sağlar.
ExecutionContext nasıl işler?
ExecutionContext.Capture() kullanarak ExecutionContext yakalayın. ExecutionContext.Run kullanarak bir temsilcinin yürütülmesi sırasında geri yükleyin.
static void ExecutionContextCaptureDemo()
{
// Capture the current ExecutionContext
ExecutionContext? ec = ExecutionContext.Capture();
// Later, run a delegate within that captured context
if (ec is not null)
{
ExecutionContext.Run(ec, _ =>
{
// Code here sees the ambient state from the point of capture
Console.WriteLine("Running inside captured ExecutionContext.");
}, null);
}
}
Sub ExecutionContextCaptureExample()
' Capture the current ExecutionContext
Dim ec As ExecutionContext = ExecutionContext.Capture()
' Later, run a delegate within that captured context
If ec IsNot Nothing Then
ExecutionContext.Run(ec,
Sub(state)
' Code here sees the ambient state from the point of capture
Console.WriteLine("Running inside captured ExecutionContext.")
End Sub, Nothing)
End If
End Sub
Zaman uyumsuz işlemleri başlatan .NET'teki tüm API'ler (Run, QueueUserWorkItem, BeginRead ve diğerleri), ExecutionContext yakalar ve geri çağırmanızı çağırırken depolanan bağlamı kullanır. Bir iş parçacığındaki durumu yakalama ve başka bir iş parçacığında geri yükleme işlemi, "executionContext akışının" anlamıdır.
SynchronizationContext nedir?
SynchronizationContext, çalışmanın gerçekleşmesini istediğiniz hedef ortamı temsil eden bir soyutlamadır. Farklı UI çerçeveleri kendi uygulamalarını sağlar:
- Windows Forms, Post geçersiz kılar ve
Control.BeginInvokeçağırmak içinWindowsFormsSynchronizationContextsağlar. - WPF, Post'i
Dispatcher.BeginInvoke'yi çağırmak amacıyla geçersiz kılanDispatcherSynchronizationContextsağlar. - ASP.NET (.NET Framework'te)
HttpContext.Currentkullanılabilir olmasını sağlayan kendi bağlamını sağladı.
Çerçeveye özgü hazırlama API'leri yerine kullanarak SynchronizationContext , UI çerçeveleri arasında çalışan bileşenler yazabilirsiniz:
static class SyncContextExample
{
public static void DoWork()
{
// Capture the current SynchronizationContext
SynchronizationContext? sc = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(_ =>
{
// ... do work on the ThreadPool ...
if (sc is not null)
{
sc.Post(_ =>
{
// This runs on the original context (e.g. UI thread)
Console.WriteLine("Back on the original context.");
}, null);
}
});
}
}
Class SyncContextExample
Public Shared Sub DoWork()
' Install a custom SynchronizationContext for demonstration
Dim customContext As New SimpleSynchronizationContext()
SynchronizationContext.SetSynchronizationContext(customContext)
' Capture the current SynchronizationContext
Dim sc As SynchronizationContext = SynchronizationContext.Current
ThreadPool.QueueUserWorkItem(
Sub(state)
' ... do work on the ThreadPool ...
If sc IsNot Nothing Then
sc.Post(
Sub(s)
' This runs on the original context (e.g. UI thread)
Console.WriteLine("Back on the original context.")
End Sub, Nothing)
Else
Console.WriteLine("No SynchronizationContext was captured.")
End If
End Sub)
End Sub
End Class
' A minimal SynchronizationContext for demonstration purposes
Class SimpleSynchronizationContext
Inherits SynchronizationContext
Public Overrides Sub Post(d As SendOrPostCallback, state As Object)
' Queue the callback to run on a thread pool thread
ThreadPool.QueueUserWorkItem(
Sub(s)
d(state)
End Sub)
End Sub
End Class
SynchronizationContext Yakalama
Bir SynchronizationContext yakaladığınızda, başvuruyu SynchronizationContext.Current okuyup daha sonra kullanmak üzere depolarsınız. Ardından yakalanan başvuru üzerinde Post çağırarak çalışmayı o ortama geri zamanlarsınız.
Flowing ExecutionContext ile SynchronizationContext'i kullanma karşılaştırması
Her iki mekanizma da bir iş parçacığından durum yakalamayı içerse de, farklı amaçlara hizmet eder:
- ExecutionContext akışı , ortam durumunu yakalama ve temsilcinin yürütmesi sırasında aynı durumu geçerli hale getirme anlamına gelir. Temsilci, çalıştığı her yerde işlev görür; durum onu izler.
- SynchronizationContext kullanmak, bir zamanlama hedefi yakalamak ve temsilcinin nerede çalıştırılacağına karar vermek için bunu kullanmak anlamına gelir. Yakalanan bağlam, temsilcinin nerede çalıştığını denetler.
Kısaca: ExecutionContext "Hangi ortamın görünür olması gerekir?" yanıtını verirken SynchronizationContext "kod nerede çalıştırılmalıdır?" yanıtını verir.
Asenkron/await her iki bağlamla nasıl etkileşim kurar?
Altyapı her async/await iki bağlamla da otomatik olarak ancak farklı şekillerde etkileşim kurar.
ExecutionContext her zaman devredilir
bir await yöntemi askıya alırsa (awaiter'ın IsCompleted döndürdüğü falseiçin), altyapı bir ExecutionContextyakalar. Yöntem devam ettiğinde, devam yakalanan bağlam içinde çalışır. Bu davranış zaman uyumsuz yöntem oluşturucularında yerleşik olarak bulunur (örneğin, AsyncTaskMethodBuilder) ve ne tür beklenebilir kullandığınızdan bağımsız olarak geçerlidir.
SuppressFlow() var, ancak ConfigureAwait(false) gibi beklemeye özgü bir anahtar değildir. Bastırma etkinken kuyruğa alınan işler için ExecutionContext yakalamayı engeller. Zaman uyumsuz yöntem oluşturucularına bir devamlılık için yakalanan await geri yüklemeyi atlamalarını söyleyen programlama modeli başınaExecutionContext seçeneği sağlamaz. Bu tasarım kasıtlıdır çünkü ExecutionContext, zaman uyumsuz bir dünyada iş parçacığı yerel semantiği simülasyonu sağlayan altyapı düzeyinde destektir. Çoğu geliştirici, bunun üzerinde düşünmek zorunda kalmaz.
Görev bekleyicileri SynchronizationContext'i yakalar
Task ve Task<TResult> için bekleyiciler SynchronizationContext desteği içerir. Asenkron yöntem oluşturucular bu desteği sağlamaz.
Bir görev yaptığınızda await :
- Awaiter SynchronizationContext.Current'yi denetler.
- Bir bağlam varsa, awaiter onu yakalar.
- Görev tamamlandığında, devam, tamamlanan iş parçacığında veya iş parçacığı havuzunda çalışmak yerine yakalanan bağlama tekrar gönderilir.
Bu davranış, await "sizi olduğunuz yere geri getirme" yöntemidir. Örneğin, bir masaüstü uygulamasında kullanıcı arabirimi iş parçacığında devam etme.
ConfigureAwait işlevi, SynchronizationContext yakalamayı kontrol eder
Eğer hazırlama davranışını istemiyorsanız, ConfigureAwait ile false çağrı yapmayın.
await task.ConfigureAwait(false);
continueOnCapturedContext öğesini false olarak ayarladığınızda, awaiter SynchronizationContext'yi denetlemez ve görev tamamlandığında (genellikle bir iş parçacığı havuzu iş parçacığında), devam çalışır. Kütüphane geliştiricileri, kodun yakalanan bağlamda devam etmesi gerekmediği sürece, her await üzerinde ConfigureAwait(false) kullanmalıdır.
SynchronizationContext.Current, beklemeler arasında akmıyor
Bu nokta en önemli noktadır: SynchronizationContext.Current bekleme noktaları arasında akış yapmaz. Çalışma zamanında eşzamansız yöntem oluşturucuları, SynchronizationContext'ın ExecutionContext'in bir parçası olarak akışını açıkça engelleyen dahili aşırı yüklemeler kullanır.
Bu neden önemli?
Teknik olarak, SynchronizationContext içerebilen alt bağlamlardan ExecutionContext biridir. bir parçası olarak ExecutionContext akıyorsa, bir iş parçacığı havuzu iş parçacığında yürütülen kod, bağlam akış aracılığıyla "sızdırıldığından" kullanıcı arabirimini SynchronizationContextCurrent olarak görebilir, bu iş parçacığı, ui iş parçacığı olduğu için değil. Bu değişiklik, SynchronizationContext.Current ifadesinin anlamını "şu anda içinde bulunduğum ortam"dan "çağrı zincirinde geçmişte bir yerde var olan ortam"a dönüştürür.
Task.Run örneği
işi iş parçacığı havuzuna aktaran kodu göz önünde bulundurun. Burada açıklanan kullanıcı arayüzü iş parçacığı davranışı, yalnızca SynchronizationContext.Current null olmadığında, yani bir kullanıcı arayüzü uygulamasında uygulanır.
static class TaskRunExample
{
public static async Task ProcessOnUIThread()
{
// This method is called from a thread with a SynchronizationContext.
// Task.Run offloads work to the thread pool.
string result = await Task.Run(async () =>
{
string data = await DownloadAsync();
// Compute runs on the thread pool, not the original context,
// because SynchronizationContext doesn't flow into Task.Run.
return Compute(data);
});
// Back on the original context (the continuation is posted back).
Console.WriteLine(result);
}
private static async Task<string> DownloadAsync()
{
await Task.Delay(100);
return "downloaded data";
}
private static string Compute(string data) =>
$"Computed: {data.Length} chars";
}
Class TaskRunExampleClass
Public Shared Async Function ProcessOnUIThread() As Task
' If a SynchronizationContext is present when this method starts,
' the outer await captures it. Task.Run still offloads work to the thread pool.
Dim result As String = Await Task.Run(
Async Function()
Dim data As String = Await DownloadAsync()
' Compute runs on the thread pool, not the caller's context,
' because SynchronizationContext doesn't flow into Task.Run.
Return Compute(data)
End Function)
' Resume on the captured context, if one was available.
Console.WriteLine(result)
End Function
Private Shared Async Function DownloadAsync() As Task(Of String)
Await Task.Delay(100)
Return "downloaded data"
End Function
Private Shared Function Compute(data As String) As String
Return $"Computed: {data.Length} chars"
End Function
End Class
Bir konsol uygulamasında SynchronizationContext.Current genellikle null şeklindedir, bu nedenle kod parçacığı gerçek bir kullanıcı arabirimi iş parçacığında devam etmez. Bunun yerine kod parçacığı, kuralı kavramsal olarak gösterir: Eğer bir kullanıcı arabirimi SynchronizationContext noktalar await arasında akıyorsa, await içindeki delegeye geçirilen, o kullanıcı arabirimi bağlamını Current olarak görür. Devamı await DownloadAsync() kullanıcı arayüzü iş parçacığına gönderildiğinde, Compute(data) iş parçacığı havuzu yerine UI iş parçacığında çalışmasına neden olur. Bu davranış, Task.Run çağrısının amacını bozar.
Çalışma zamanı SynchronizationContext akışını ExecutionContext içinde bastırdığından, Task.Run içindeki await dış kullanıcı arabirimi bağlamını devralmıyor ve devamlılık, beklendiği gibi iş parçacığı havuzunda çalışmayı sürdürmektedir.
Özet
| Görünüş | Yürütme Bağlamı | SynchronizationContext |
|---|---|---|
| Purpose | Ortam durumunu eşzamansız sınırlar arasında taşır | Bir hedef zamanlayıcıyı temsil eder (burada kod çalıştırılmalıdır) |
| (Tarafından) Yakalandı | Zaman uyumsuz yöntem oluşturucuları (altyapı) | Görev bekleyenler (await task) |
| Akışlar arasında bekleme mi var? | Evet, her zaman | Hayır— yakalanan ve gönderilen, akışı yapılmayan |
| Gizleme API'si |
ExecutionContext.SuppressFlow (gelişmiş; nadiren gereklidir) |
ConfigureAwait(false) |
| Scope | Tüm beklenebilir nesneler |
Task ve Task<TResult> (özel bekleyiciler benzer mantığı ekleyebilir) |