Sanırım burada birkaç şey karıştı. İstediğiniz şey zaten kullanılabilir System.Threading.Tasks
, async
ve await
C # 5'de aynı özellik için biraz daha hoş bir sözdizim şekeri sağlayacaksınız.
Bir Winforms örneği kullanalım - forma bir düğme ve bir metin kutusu bırakın ve şu kodu kullanın:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew<int>(() => DelayedAdd(5, 10))
.ContinueWith(t => DelayedAdd(t.Result, 20))
.ContinueWith(t => DelayedAdd(t.Result, 30))
.ContinueWith(t => DelayedAdd(t.Result, 50))
.ContinueWith(t => textBox1.Text = t.Result.ToString(),
TaskScheduler.FromCurrentSynchronizationContext());
}
private int DelayedAdd(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Çalıştırın ve o (a) UI iş parçacığı ve (b) Hata zamanki "geçerli değil çapraz iplik operasyonu" alamadım engellemez göreceksiniz - Kaldırmak sürece TaskScheduler
son gelen argüman ContinueWith
, içinde hangi durumda olacak.
Bu bataklık standart devam geçiş tarzı . Sihir TaskScheduler
sınıfta ve özellikle tarafından alınan örnekte gerçekleşir FromCurrentSynchronizationContext
. Bunu herhangi bir sürdürmeye geçirin ve devam etmenin FromCurrentSynchronizationContext
yöntem olarak adlandırılan herhangi bir iş parçacığında (bu durumda UI iş parçacığı) çalışması gerektiğini söylersiniz .
Bekleyenler, hangi iş parçacığı üzerinde başladıklarının ve hangi iş parçacığının gerçekleşmesi gerektiğinin farkında olmaları açısından biraz daha karmaşıktır. Yukarıdaki kod biraz daha doğal olarak yazılabilir :
private async void button1_Click(object sender, EventArgs e)
{
int a = await DelayedAddAsync(5, 10);
int b = await DelayedAddAsync(a, 20);
int c = await DelayedAddAsync(b, 30);
int d = await DelayedAddAsync(c, 50);
textBox1.Text = d.ToString();
}
private async Task<int> DelayedAddAsync(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Bu iki çok benzer görünmelidir ve aslında onlar vardır çok benzer. DelayedAddAsync
Yöntemi şimdi bir döner Task<int>
yerine bölgesinin int
ve böylece await
sadece bu her biri üzerine devamlılık tokat. Temel fark, her satırdaki senkronizasyon bağlamından geçmesidir, bu nedenle bunu son örnekte yaptığımız gibi açıkça yapmanız gerekmez.
Teoride farklılıklar çok daha önemlidir. İkinci örnekte, button1_Click
yöntemdeki her satır aslında UI iş parçacığında yürütülür, ancak görevin kendisi ( DelayedAddAsync
) arka planda çalışır. İlk örnekte, her şey çalışır içinde arka , hariç için atama için textBox1.Text
biz açıkça UI parçacığının senkronizasyon bağlamına bağlı olduğunuz.
Bu gerçekten ilginç olan şey await
- bir bekleyicinin herhangi bir engelleme çağrısı olmadan aynı yönteme girip çıkabilmesi . Siz çağırırsınız await
, mevcut iş parçacığı iletileri işlemeye geri döner ve bittiğinde, bekçi tam olarak kaldığı yerden, bıraktığı aynı iş parçacığında alır. Ama sorudaki Invoke
/ BeginInvoke
kontrastınız açısından, ' Bunu uzun zaman önce bırakmış olmanız gerektiğini söylediğim için üzgünüm.
await
işlevsellik söz konusu olduğunda , bu pek olası görünmemektedir . Sadece için sözdizimsel şeker bir sürü devamı geçmesiyle . Muhtemelen WinForms'a yardımcı olması gereken ilgisiz bazı iyileştirmeler var mı? Bu, .NET çerçevesi altında olsa da, özellikle C # değil.