Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors

המדריך לתכנות מקבילי בשפת C Sharp – פרק ז' – Semaphore & SemaphoreSlim

בפרקים הקודמים של המדריך למדנו כיצד ביכולתנו להגיע לסינכרוניות בתכנות מקבילי על ידי שימוש בנעילות אקסקלוסיביות (Lock, Monitor & Mutex).
בפרק זה נלמד על נעילות שאינן אקסקלוסיביות – Semaphore & SemaphoreSlim.

כפי שראינו, לנעילה אקסקלוסיבית יש את היכולת לנעול את המקטע באופן אקסקלוסיבי עבור Thread אחד בודד בזמן נתון,
אך מה אם היינו מעוניינים להעניק גישה ליותר מ-Thread אחד?

לשם כך, בשפת #C קיימות המחלקות Semaphore ו-SemaphoreSlim.
במאמר זה נבאר את אופן פעולתן של מחלקות אלו ונציג דוגמאות קוד.

מחלקת Semaphore

באמצעות Mutex הגבלנו את הגישה ל-Thread בודד בתוך תהליך בודד,
כאשר באמצעות מחלקת Semaphore נוכל לשלוט במספר ה-Threads שיכולים לגשת למשאב המשותף,
או במילים אחרות – נגביל את הגישה למקטע הקריטי ל-Thread אחד או יותר.

Multithreading - Semaphore
Multithreading – Semaphore

כעת נעבור ונכיר את הבנאים והמתודות של המחלקה.

הבנאים של מחלקת Semaphore

כפי שניתן לראות בתמונה, למחלקה זו 3 בנאים:

Semaphore(int initialCount, int maximumCount) – מאתחל מופע חדש של המחלקה, כאשר כפרמטרים נשלח לו מספר התחלתי ומספר מקסימלי של בקשות גישה למקטע.

Semaphore(int initialCount, int maximumCount, string? name) – מאתחל את המופע, כאשר כפרמטרים נשלח לו מספר התחלתי ומספר מקסימלי של בקשות גישה,
הפרמטר השלישי שנשלח הוא שם למופע.

Semaphore(int initialCount, int maximumCount, string name, out bool createdNew) – מאתחל מופע, כאשר כפרמטרים נשלח לו מספר התחלתי ומספר מקסימלי של בקשות גישה,
הפרמטר השלישי שנשלח הוא שם למופע והרביעי הוא Out Parameter בוליאני שנועד לווידוא כי אכן נוצר מופע חדש של Semaphore.

המתודות של מחלקת Semaphore

OpenExisting(string name) – במתודה סטטית זו נשתמש על מנת להקנות גישה לאובייקט Semaphore לפי השם שלו – במידה והוא קיים.

TryOpenExisting(string name, [NotNullWhen(true)] out Semaphore? result) – מתודה סטטית אשר משמשת להקניית גישה לאובייקט Semaphore לפי השם שלו – במידה והוא קיים.
אם האופרציה הצליחה יוחזר לנו האובייקט כתוצאה, במקרה ונכשלה יוחזר לנו Null.

()Release – יוצא מה-Semaphore ומחזיר לנו את ספירת הכניסות האחרונה שנרשמה.

Release(int releaseCount) – יוצא מה-Semaphore מספר פעמים (כמספר הפעמים שנשלח כפרמטר),
ומחזיר לנו את ספירת הכניסות האחרונה שנרשמה.

שימו לב כי מחלקה זו יורשת ממחלקת WaitHandle אשר לה מתודות שנועדו לניהול כניסה-המתנה של אובייקט למקטע קריטי,
אם כך נוכל להשתמש במתודות שלה לצרכינו, למשל ב-WaitOne:

()WaitOne – במתודה זו נוכל להשתמש על מנת להקנות גישה למקטע הקריטי בשילוב עם אובייקט ה-Semaphore שלנו,
כאשר אם מספר ה-Threads המנוהלים כרגע גדול מ-0.

שימו לב ל-Code Snippet הבא שבו נדגים כיצד מחלקת Semaphore מאפשרת לנו להכניס יותר מ-Thread אחד במקביל למקטע הקריטי:

using System.Threading;

namespace ThreadingDemo
{
    public class Program
    {
        public static Semaphore semaphore = new(2, 3);

        static void Main(string[] args)
        {
            for (int i = 1; i <= 10; i++)
            {
                Thread threadObject = new(DoSomething)
                {
                    Name = "Thread " + i
                };
                threadObject.Start(i);
            }
        }
        static void DoSomething(object id)
        {
            Console.WriteLine(Thread.CurrentThread.Name + " wants to enter the critical section for processing");
            try
            {
                semaphore.WaitOne();
                Console.WriteLine(Thread.CurrentThread.Name + " is Doing its work");
                Thread.Sleep(1000);
                Console.WriteLine(Thread.CurrentThread.Name + "exits.");
            }
            finally
            {
                semaphore.Release();
            }
        }
    }
}

 

Multithreading - Semaphore
Multithreading – Semaphore

כפי שניתן לראות בדוגמא זו, הגישה למקטע הקריטי ניתנה ליותר מ-Thread אחד בודד בזמן נתון.

מחלקת SemaphoreSlim

במחלקת SemaphoreSlim נוכל להשתמש על מנת לסנכרן את הגישה גם ליותר מתהליך אחד בודד בזמן נתון בדומה ל-Semaphore.
זוהי למעשה אלטרנטיבה "קלה" יותר של Semaphore.

Multithreading - SemaphoreSlim
Multithreading – SemaphoreSlim

כעת ננסה לשנות את התוכנית שלנו כך שבמקום שנשתמש ב-Semaphore, נשתמש ב-SemaphoreSlim:

using System.Threading;

namespace ThreadingDemo
{
    public class Program
    {
        static SemaphoreSlim semaphore = new(initialCount: 4);
        static void Main(string[] args)
        {
            for (int i = 1; i <= 5; i++)
            {
                int count = i;
                Thread t = new(() => DoSomething("Thread " + count));
                t.Start();
            }
        }
        static void DoSomething(string name)
        {
            Console.WriteLine($"{name} waits to access");
            semaphore.Wait();
            Console.WriteLine($"{name} was granted access");
            Thread.Sleep(2000);
            Console.WriteLine($"{name} is completed");
            semaphore.Release();
        }
    }
}

כפי שניתן לראות, בשורה 7 הגדרנו שהמקסימום Threads שיקבלו גישה בו זמנית יהיה 4:

Multithreading - SemaphoreSlim
Multithreading – SemaphoreSlim

עד עתה ראינו כיצד ניתן לבצע נעילות אקסקלוסיביות ונעילות שאינן אקסקלוסיביות למשאב משותף,
אך תארו לכם מצב שבו למשל 2-Threads מחכים אחד לשני שיסיימו את הפעולה.
מקרה כזה נקרה DeadLock והפרק הבא של המדריך יוקדש לנושא זה.

לקריאה מורחבת על Thread ו-Threading באתר של מייקרוסופט יש ללחוץ כאן.

רוצים לשתף את המדריך?

אהבתכם את המדריך? פתר לכם תקלה?

גולשים יקרים, רוב התכנים המוצגים באתר נכתבים בהתנדבות מלאה מתוך כוונה להנגיש מידע עבורכם. אם נתקלתם במדריך חינמי שפתר לכם תקלה או לימד אתכם משהו חדש שלא ידעתם, וברצונכם לתגמל את כותב המדריך או סתם להזמין אותו לכוס קפה, הינכם יותר ממוזמנים לתרום.

כתיבת תגובה

הזמינו אותי לכוס קפה
buy me coffee

אהבתכם את המדריך? פתר לכם תקלה? הזמינו את כותב המדריך לכוס קפה

גולשים יקרים, רוב התכנים המוצגים באתר נכתבים בהתנדבות מלאה מתוך כוונה להנגיש מידע עבורכם. אם נתקלתם במדריך חינמי שפתר לכם תקלה או לימד אתכם משהו חדש שלא ידעתם, וברצונכם לתגמל את כותב המדריך או סתם להזמין אותו לכוס קפה, הינכם יותר ממוזמנים לתרום.