CodeGym /Kurslar /C# SELF /Diaqnostika üçün ən yaxşı təcrübələr və alətlər

Diaqnostika üçün ən yaxşı təcrübələr və alətlər

C# SELF
Səviyyə, Dərs
Mövcuddur

1. Davamlı çoxthreadli kodun sirri

Çoxthreadliyi eyni maşını eyni vaxtda düzəldən bir neçə işçi komandası kimi təsəvvür etsək, birinin başqasının alətini götürməsi təmirin dayanmasına səbəb olur. Koddakı paylaşılan məlumatlarla laqeyd davranmaq gizli səhvlərə gətirib çıxarır, hansı ki yalnız "savaştə" üzə çıxa bilər.

Bu mühazirədə — kart evi kimi çökməyən çoxthreadli kod necə yazmaq, və əgər nəsə səhv gedərsə hansı alətlər kömək edəcək.

1. Kritik bölmələri (lock) minimum saxlayın

Lock daxilində daha az kod olduqca yaxşıdır. Bir thread lock-u saxladığı müddətcə digərləri gözləyir.

Nümunə:


// PLOXO: Bütün biznes-logic lock daxilində — bütün threadlər gözləyir
lock(_locker)
{
    // Uzun əməliyyat (paylaşılan resursa aid deyil)
    Thread.Sleep(500);
    counter++;
}

// YAXŞI: Yalnız minimal lazım olan əməliyyat lock içindədir
// bütün ağır iş lock xaricindədir
Thread.Sleep(500);
lock(_locker)
{
    counter++;
}

Real həyat: Əgər lock daxilində şəbəkə çağırışı və ya uzun hesablamadırsa, performans kəskin düşür.

2. Lock açarı kimi universal obyektlərdən istifadə etməyin

lock(this) və ya lock(typeof(MyClass)) pis fikirdir.

Niyə? Əgər başqa biri eyni obyekti öz lock-u üçün istifadə edərsə, qarşılıqlı bloklanma və ya gizli buglar alınar. Həmişə ayrıca privat obyekt istifadə edin:

private readonly object _locker = new object();

lock(_locker)
{
    // Sizin əməliyyatlar
}

Qadağan olunur: stringlər (string), public sahələr, dəyər-tip obyektləri.

3. Tutuqlanan resursları azad etmək üçün həmişə try...finally istifadə edin

Hər hansı Mutex, semafor, ReaderWriterLockSlim tutulması — finally içində azad edilməlidir.

_mutex.WaitOne();
try
{
    // Kritik bölmə
}
finally
{
    _mutex.ReleaseMutex();
}

4. Sinxronizasiyanı sui-istifadə etməyin

Yalnız real paylaşılan resurslara (məsələn kolleksiyalara) çıxışı sinxronizasiya edin, "hər ötürmə" üçün yox. Artıq lock-lar kodu gözləmə sıraçına çevirir.

5. Thread-safe kolleksiyalar və tiplərdən istifadə edin

.NET çoxthreadli ssenarilər üçün xüsusi kolleksiyalar təqdim edir: ConcurrentDictionary, ConcurrentQueue, ConcurrentBag, BlockingCollection və s. Onlar daxilində özlərini artıq qoruyurlar.

using System.Collections.Concurrent;

ConcurrentDictionary<int, string> users = new ConcurrentDictionary<int, string>();
users.TryAdd(1, "Vasya");
users[2] = "Petya";

6. Deadlock (qarşılıqlı bloklanma) ehtiyatlı olun

Tipik tələf — bir neçə lock-u fərqli sıralarda tutmaq.

// Thread 1
lock(obj1)
{
    lock(obj2)
    {
        // Nəsə edirik
    }
}

// Thread 2
lock(obj2)
{
    lock(obj1)
    {
        // Nəsə edirik
    }
}

Məsləhət: Bütün threadlərdə lock-ları həmişə eyni sırayla alın.

7. Mümkün olduqda immutable state (dəyişməz vəziyyət) istifadə edin

Obyekt yaradıldıqdan sonra vəziyyətini dəyişmirsə — onu hər hansı thread-dən təhlükəsiz oxumaq olar. Nümunələr: string, Tuple, DateTime, öz Read-only DTO-lar.

2. Çoxthreadlik problemlərinin diaqnostikası üçün alətlər

Sinxronizasiya səhvləri hiyləgərdir: nadir və proqnozlaşdırılmaz şəkildə ortaya çıxır. Onları tutmaq və analiz etmək üçün alətlərdən və yanaşmalardan istifadə edin.

1. Hadisələrin və threadlərin loglanması

Cari Thread.ManagedThreadId və əsas əməliyyatları loglayın — "kimin və nə vaxt" kritik bölməyə girdiyini/çıxdığını başa düşmək üçün sadə üsuldur.

Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Všel v kritik bölmə");
// ...
Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Výšel iz kritik bölmə");

Real tətbiqlər üçün Microsoft.Extensions.Logging, NLog, Serilog istifadə edin.

2. Thread Sanitizer & Race Detector

.NET-də tam "daxili" ThreadSanitizer yoxdur, amma faydalı alətlər var:

3. Visual Studio Diagnostics Tools

Visual Studio profilləşdiriciləri göstərməyə kömək edir:

  • tətbiqdaxili hansı threadlərin olduğunu;
  • threadlərin harada boşdaydığını (waiting);
  • harada lock və contention yaranır;
  • deadlock və contention nə vaxt meydana gəlir.

Lock istifadə qrafikasını almaq üçün trace-lər götürün.

4. Dump-analitika və WinDbg

Server "asılıb"sa, proses dump-u götürün və onu WinDbg və ya dotnet-dump ilə açın. Call stack-lərə baxaraq threadlərin harda ilişdiyini və kim hansı lock-u saxladığını görmək olar.

Stack analiz nümunəsi:

0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
    1 000001d4b6f90e08          1         1 000001d4b5c941c0 000001d4b6f03458

(Dump-lara adətən deployment üzrə "təcrübəli jedilər" müraciət edir — qorxmayın, bu güclü alətdir.)

5. Yük altında unit-testlər (stress testing)

Çoxthreadli kodu paralel olaraq yüzlərlə/minlərlə iterasiyada işlədin — nadir yarışlar (race) belə daha asan tapılır.

[Test]
public void Counter_IsThreadSafe()
{
    var counter = 0;
    var locker = new object();
    var tasks = new List<Task>();
    for (int i = 0; i < 100; i++)
    {
        tasks.Add(Task.Run(() =>
        {
            for (int j = 0; j < 10000; j++)
            {
                lock (locker)
                {
                    counter++;
                }
            }
        }));
    }
    Task.WaitAll(tasks.ToArray());
    Assert.AreEqual(100 * 10000, counter);
}

6. Asserts və xüsusi yoxlamalardan istifadə

Debug zamanı düzgün vəziyyəti təmin edən yoxlamalar əlavə edin. Məsələn, eyni thread tərəfindən resursun təkrar tutulması cəhdində Debug.Assert.

3. Nəticələr və tövsiyələr

Vizual sxem: təhlükəli zona və təhlükəsizlik

graph TD
    A[Ümumi resurs] -- sinxronizasiya olmadan --> B(Vaziyatı yarış)
    A -- bloklama (lock/Mutex) --> C[Təhlükəsiz çıxış: kritik bölmə]
    C -- "çox lock-lar" --> D(Performans itkisi)
    A -- ReaderWriterLockSlim --> E{Çox oxuyan / Bir yazar}
    E -- "Oxuma" --> F[Çox thread eyni vaxtda oxuyur]
    E -- "Yazma" --> G[Yalnız biri yazır, digərləri gözləyir]

Sinxronizasiya primitivləri və onların məqsədi

Primitiv Nə üçün lazım Neçə thread keçir Proseslərarası Performans Harada istifadə etmək
lock (Monitor)
Sadə kritik bölmə 1 Xeyr Çox yüksək 99% case-lər
Mutex
Eyni bölmə, amma proseslər arasında 1 Bəli Orta Files, IPC
Semaphore
Ən çox N thread N Bəli Orta Resurs pool-ları
SemaphoreSlim
Eyni, amma daha sürətlidir, proses daxilində N Xeyr Yüksək Kodda pool-lar
ReaderWriterLockSlim
Çox oxuyanlar, bir yazar Çox/1 Xeyr Yüksək Cache-lər, konfiqurasiyalar
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Livelock-un təsadüfi fasilələrlə aradan qaldırılması
Livelock-un təsadüfi fasilələrlə aradan qaldırılması
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Qarşılıqlı blokirovkalar
Çox axınlı proqramlaşdırmada tipik problemlər
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION