1. Fire and forget nədir?
Proqramlaşdırmada fire and forget termini tapşırığın onun bitməsini gözləmədən işə salınması deməkdir. C# və .NET dünyasında bunu adətən Task ilə edirlər: tapşırığı işə salırlar, amma heç yerdə gözləmirlər (await etmirlər), referans saxlamırlar və əslində onu unudurlar.
// Düymə fon tapşırığını işə salır, amma heç yerdə await olunmur.
button.Click += (s, e) =>
{
Task.Run(() => DolgayaOperatsiya());
};
Cəlbedicidir: "gəl fon işinə davam etsin, mən öz işimi görəm". Amma belə yanaşmada, əgər tapşırığın içində istisna baş verərsə, heç kim vaxtında bundan xəbər tutmayacaq — o sakitcə itə bilər.
2. Task-də istisnaların işlənməsi necə işləyir
Klasiya: await və səhvlərin tutulması
Asinxron tapşırıqlarla işləməyin standart yolu — await-dan istifadədir. Əgər tapşırıqda səhv baş verərsə, o gözləmə nöqtəsində atılacaq:
try
{
await SomeOperationAsync(); // əgər içində Exception olsa, o catch-ə düşəcək
}
catch(Exception ex)
{
Console.WriteLine("Uups! Tapşırıqda səhv oldu: " + ex.Message);
}
Yəni tapşırığın tamamlanmasını gözlədikdə, istisnanı qaçırmayacaqsınız.
Amma fire-and-forget tapşırıqları heç kim gözləmir!
public void IshleyiGozlemedenBaslat()
{
// Tapşırıq özü-özünə işləyir. Heç kim onu gözləmir...
Task.Run(() => {
// Haradasa içində problem yaranır:
throw new InvalidOperationException("Oj, hamısı itdi!");
});
// Metod bitdi, tapşırıq sakitcə fonda işləyir.
}
Belə tapşırığın içində istisna baş verərsə, o əsas thread-də atılmayacaq. Proqram işləməyə davam edəcək, sanki heç nə olmamış kimi.
Vacib
.NET-də işlənməmiş istisna ilə tapşırıq Faulted vəziyyətinə keçir. Amma əgər siz onu gözləmirsinizsə (await, .Result, .Wait() və s.), heç kim o istisnanı oxumayacaq və o çağıran kodda görünməyəcək.
Qapağın altında nə baş verir?
Heç kəs gözləmədiyi tapşırıqlar üçün tək ümid — TaskScheduler.UnobservedTaskException hadisəsidir. Bu hadisə GC (garbage collector) işlədikdə və işlənməmiş istisnalı tapşırıq tapdıqda tetiklenir. Amma bu dərhal olmur və gözlədiyiniz yerdə olmaya bilər — ona güvənmək olmaz.
3. Demonstrasiya: Fire-and-forget səhvi
// Nümunə: Main-dən birbaşa fire-and-forget tapşırıq işə salırıq
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
FireAndForgetExample();
Console.WriteLine("Əsas thread işləməyə davam edir...");
// Tapşırığa bitmək üçün vaxt verək
Task.Delay(2000).Wait();
}
static void FireAndForgetExample()
{
Task.Run(() =>
{
Console.WriteLine("Fire-and-forget tapşırıq başladı!");
Task.Delay(500).Wait();
throw new InvalidOperationException("Fire-and-forget tapşırığın içində səhv!");
});
}
}
Bu kodu işlətsəniz... xüsusi bir şey baş verməyəcək. Səhv baş verəcək, amma proqram bundan xəbərdar olmayacaq. Bəzən IDE-nin Output pəncərəsində xəbərdarlıq görünə bilər, amma istifadəçiyə — heç nə.
Real layihələrdə niyə təhlükəlidir?
- Çətin reproduksiya olunan səhvlər ("bəzən işləmir — niyə mənə məlum deyil").
- Məlumatın və ya loqikanın sakitcə itməsi (məsələn, müştəriyə məktub göndərilmədi).
- Production-da monitorinq yoxdursa problemlərin siqnalı olmaması.
4. Fire-and-forget-də səhvləri düzgün işləmək yolları
Loqlama və səhvlərin tapşırığın içində işlənməsi
Minimum təhlükəsiz səviyyə — istisnaları birbaşa fire-and-forget tapşırığının içində tutmaq:
Task.Run(() =>
{
try
{
// Sizin uzun/təhlükəli kodunuz
throw new InvalidOperationException("Bir şey səhv getdi!");
}
catch (Exception ex)
{
// Səhvi loqlayırıq və ya istifadəçiyə məlumat veririk
Console.WriteLine("Fire-and-forget: tutulan istisna: " + ex.Message);
// Fayla yazmaq, alert sistemi ilə inteqrasiya və s. mümkündür
}
});
Asinxron void metodları (və niyə bu yaxşı fikir deyil)
async void TehlikeliFireAndForget()
{
// Nəsə təhlükəli
throw new Exception("Bum!");
}
async void metodlar faktiki olaraq fire-and-forget-dir: onları gözləmək mümkün deyil, onlar Task qaytarmır. Onlardan gələn istisnalar tətbiqin qlobal handler-ına (məsələn, AppDomain.UnhandledException) düşür və çox vaxt prosesin çöküşünə səbəb ola bilər. async void-dan yalnız event handler-larda istifadə edin — və yenə ehtiyatla.
Səhvlərin işlənməsi üçün yardımcı metodlardan istifadə
Fire-and-forget-in təhlükəsiz işə salınmasını ayrıca wrapper-a çıxarmaq rahatdır:
// Fire-and-forget-in təhlükəsiz işə salınması üçün universal metod
public static void RunSafeFireAndForget(Func<Task> taskFactory)
{
Task.Run(async () =>
{
try
{
await taskFactory();
}
catch (Exception ex)
{
// İstisnanı loqlayırıq
Console.WriteLine("Fire-and-forget (safe): " + ex);
// Monitoring sisteminə göndərmək kimi əlavə addımlar mümkün!
}
});
}
// İstifadə:
RunSafeFireAndForget(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException("Fire-and-forget içində!");
});
Həqiqi həyat nümunəsi: email göndərilməsi
// Göndərmə düyməsi:
private void buttonSend_Click(object sender, EventArgs e)
{
Task.Run(() => SendEmail());
}
// Göndərmə metodu:
private void SendEmail()
{
try
{
// Burada real göndərmə ola bilər
throw new Exception("SMTP-server əlçatmazdır!");
}
catch (Exception ex)
{
// Loqlama
File.AppendAllText("errors.log", $"Email göndərmə səhvi: {ex.Message}\n");
}
}
5. UnobservedTaskException ilə nə baş verir?
Ekstremal halda .NET TaskScheduler.UnobservedTaskException hadisəsini təmin edir. Bu hadisə tapşırıq səhvlə bitdikdə, heç kim onu gözləmədikdə və tapşırıq obyektini GC topladıqda çağırılır. Buna etibar etmək olmaz — bu "son şans" mexanizmidir.
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
Console.WriteLine("Qlobal UnobservedTaskException: " + e.Exception);
e.SetObserved(); // Bunu çağırmağı unutmayın, yoxsa tətbiq avariya ilə bağlana bilər!
};
Əlavə məlumat: TaskScheduler.UnobservedTaskException.
6. Faydalı nüanslar
Yanaşmaların sxematik müqayisəsi
| Yanaşma | İstisnalar işlənib? | Səhvləri harada tutmaq | Səhvi "itirmək" riski |
|---|---|---|---|
|
Bəli | Çağıran kodda | Aşağı |
| Fire-and-forget try/catch-siz | Xeyr | Heç yerdə | Çox yüksək |
| Fire-and-forget try/catch ilə | Bəli | Tapşırığın özündə | Aşağı (əgər loqlayırsınızsa) |
| async void-metod | Xeyr (qlobalə düşür) | Qlobal handler | Yüksək |
Fire-and-forget necə dizayn edilməlidir
- Əgər tapşırığın nəticəsi və ya vəziyyəti kritikdirsə — fire-and-forget etməyin. await və ya sonra gözləmək üçün Task saxlayın.
- Fire-and-forget yalnız həqiqətən əhəmiyyətsiz fon tapşırıqları üçün məntiqlidir (məsələn, telemetriya göndərmə).
- Həmişə fire-and-forget-i öz metoduna bükün və istisnaları tutub loqlayın.
- Mürəkkəb fon senariləri üçün növbələr və worker-lər istifadə edin: Hangfire, Quartz.NET.
Praktik tətbiq və müsahibələr
Müsahibələrdə tez-tez soruşurlar: "Fire-and-forget tapşırıqda istisna baş verərsə nə olar?" və ya "Niyə hər yerdə async void istifadə etmək olmaz?" Düzgün cavab: fon tapşırıqlarındakı səhvlərin taleyinə yalnız siz cavabdehsiniz — ya tutub loqlayacaqsınız və analiz edəcəksiniz, ya da ruh şəklində səhvlər əldə edəcəksiniz.
"fire-and-forget" və await müqayisəsi
| Ssenari | Səhvlərin işlənməsi etibarlılığı | Tətbiq sahəsi |
|---|---|---|
| Adi await | Əla | Nəticə lazım olduqda və ya success/fail vacibdirsə hər yerdə |
| Fire-and-forget | Poor (əgər əllə işlənməzsə) | Yalnız həqiqətən fon və əhəmiyyətsiz tapşırıqlar üçün |
| Fire-and-forget try/catch ilə | Yaxşı (əgər loqlayırsa) | Nəticə tələb olunmasa da xətalar haqqında məlumat vacibdirsə |
Növbəti mühazirədə bir neçə nəticə qaytaran paralel tapşırıqlarda səhvlərin işlənməsini müzakirə edəcəyik. İndi isə yadınızda saxlayın: əgər hardasa "atdınızsa", hədəfə çatdığını yoxlayın!
7. Fire-and-forget tapşırıqlarla işləyərkən tipik səhvlər
Səhv №1: Fire-and-forget-də istisnaların görməməzlikdən keçirilməsi.
Yeni başlayanlar ümid edir ki, istisnalar "hər yerdə üzə çıxacaq". try-catch və loqlama olmadan onlar itir və aşkar edilməyən bug-lara səbəb olur.
Səhv №2: Event handler-lar xaricində async void-dan istifadə etmək.
Belə metodlar istisnaları qlobal handler-a atır (məsələn, AppDomain.UnhandledException), bu isə tətbiqin qəfil bağlanmasına gətirə bilər.
Səhv №3: Həddindən artıq istisna tutulması.
Tapşırığın içində bütün istisnaları tutmaq bəzi problemləri gizlədə bilər, onları çağıran kodda işləmək daha yaxşı olardı və bu debugging-i çətinləşdirə bilər.
Səhv №4: Loqlamanın laqeydliyi.
Fire-and-forget tapşırıqlarda loqlama olmadan production-da səhvlər barədə bilməyəcəksiniz.
GO TO FULL VERSION