آشنایی با semaphore در C#

آخرین بروز رسانی: 1401/03/23

سمافور(semaphore) به زبان ساده یک متغیر غیر منفی(non-negative) است و بین ترد ها به اشتراک گذاشته می شود. سمافور یک مکانیزم سیگنال دهیست و تردی که روی سمافور منتظر است, توسط ان به تردهای دیگر علامت داده شود. از دو عملیات اتمیک، 1) انتظار، و 2) سیگنال برای همگام سازی فرآیند استفاده می کند.

یک سمافور اجازه دسترسی به منبع را یا می دهد یا نمی دهد، که بستگی به نحوه تنظیم آن دارد.

semaphore  در C#  تنها به تعداد محدودی از ترد اجازه می دهد تا وارد یک بخش بحرانی شوند .  Semaphore عمدتاً در سناریوهایی استفاده می شود که در آن تعداد منابع محدودی داریم و باید تعداد ترد هایی را که می توانند از آن استفاده کنند محدود کنیم.

سمافور چگونه کار می کند
سمافورها متغیرهای Int32 هستند که در منابع سیستم عامل ذخیره می شوند. هنگامی که شی سمافور را مقداردهی اولیه می کنیم، با عدد مقداردهی اولیه می کنیم. این عدد ترد هایی را که می توانند وارد بخش بحرانی شوند محدود می کند.
هنگامی که یک ترد وارد یک بخش بحرانی می شود، متغیر Int32 را با 1 کاهش می دهد و زمانی که یک ترد از یک بخش بحرانی خارج می شود، متغیر Int32 را با 1 افزایش می دهد.

وقتی متغیر Int32 0 باشد، هیچ رشته ای نمی تواند وارد بخش بحرانی شود.

در زیر سینتکس اولیه سازی سمافور C# آمده است.

var semaphoreObject = new Semaphore(initialCount: 0, maximumCount: 5);

شی سمافور را با دو پارامتر مقداردهی اولیه می کنیم:

      • Maximum count تعیین می کند که چه تعداد ترد حداکثر می تواند وارد یک بخش بحرانی شود.
      • InitialCount مقدار متغیر Int32 را تنظیم کرد. به عنوان مثال اگر حداکثر تعداد را 3 و تعداد اولیه را 0 قرار دهیم. یعنی 3 ترد از قبل در بخش بحرانی هستند. اگر حداکثر تعداد را 3 و تعداد اولیه را 3 قرار دهیم، به این معنی است که حداکثر 3 ترد می توانند وارد یک بخش بحرانی شوند و در حال حاضر هیچ رشته ای در بخش بحرانی وجود ندارد.

 

از سمافور بین چندین فرآیند استفاده می شود
سمافور سازنده دیگری دارد که string را به عنوان پارامتر می گیرد. این پارامتر رشته یک string منحصر به فرد است که برای استفاده از سمافور بین چندین فرآیند استفاده می شود.

در زیر نحو ایجاد سمافور آمده است.

var semaphoreObject = new Semaphore(initialCount: 0, maximumCount: 5, name: "MyUniqueNameApp");

 

متد WaitOne
تردها می توانند با استفاده از متد WaitOne وارد بخش بحرانی شوند. آنها متد WaitOne شی سمافور  را کال میکنن. اگر متغیر Int32 که توسط سمافور نگهداری می شود بزرگتر از 0 باشد، اجازه می دهد تا رشته فراخوانی وارد شود.

در زیر نحو فراخوانی متد WaitOne آمده است.

semaphoreObject.WaitOne();

در overload  دیگری از متد WaitOne ، می‌توانیم بازه زمانی را بگذرانیم که یک ترد می‌تواند منتظر دریافت سیگنال از سمافور باشد. اگر متد در مدت زمانی مشخص شده داخلی سیگنال دریافت نکرده باشد، مقدار نادرست را برمی گرداند

bool isSignalled = semaphoreObject.WaitOne(TimeSpan.FromSeconds(4));

در مثال بالا، اگر thread فراخوانی سیگنال را در مدت 4 ثانیه مشخص دریافت نکند، false را برمی‌گرداند. اگر سیگنال دریافت کند، true را برمی‌گرداند.


متد Release
هنگامی که یک ترد از بخش بحرانی خارج می شود، باید متد Release را فراخوانی کند تا شمارنده نگهداری شده توسط شی سمافور افزایش یابد. این اجازه می دهد تا موضوعات انتظار وارد یک بخش بحرانی شوند.

semaphoreObject.Release();

به‌طور پیش‌فرض Release شمارنده را فقط 1 افزایش می‌دهد. این بدان معناست که فقط یک ترد از بخش بحرانی خارج می‌شود. همچنین می‌توانیم پارامتری را به متد Release ارسال کنیم تا مشخص کنیم که واقعاً چند رشته خروجی هستند.

semaphoreObject.Release(3);

در کد بالا 3 را بعنوان پارامتر به متد Release پاس می کنیم. این به شی سمافور اطلاع می دهد که در واقع 3 ترد از بخش بحرانی خارج می شوند. بنابراین شی سمافور شمارنده را 3 افزایش می دهد.

 

نمونه سمافور
در مثال زیر نحوه استفاده از شی سمافور با شی چاپگر نشان داده شده است. ما باید تعداد ترد هایی را که می توانند همزمان از شی چاپگر استفاده کنند، محدود کنیم. برای آن از شی سمافور با حداکثر تعداد 3 استفاده می کنیم.

class Program
{
    static void Main(string[] args)
    {
        Semaphore semaphoreObject = new Semaphore(initialCount: 3, maximumCount: 3, name: "PrinterApp");
        Printer printerObject = new Printer();
 
        for (int i = 0; i < 20; ++i)
        {
            int j = i;
            Task.Factory.StartNew(() =>
                {
                    semaphoreObject.WaitOne();
                    printerObject.Print(j);
                    semaphoreObject.Release();
                });
        }
        Console.ReadLine();
    }
}
 
class Printer
{
    public void Print(int documentToPrint)
    {
        Console.WriteLine("Printing document: " + documentToPrint);
        //code to print document
        Thread.Sleep(TimeSpan.FromSeconds(5));
    }
}


شی سمافور را با  عدد اولیه 3 و حداکثر 3 مقداردهی می کنیم و نام منحصر به فرد "PrinterApp" را می دهیم. حلقه for را با اجرا از 0 تا 20 شروع می کنیم و  تردها را با استفاده از TaskFactory شروع کردیم.

هر ترد قبل از استفاده از شی چاپگر، متد  WaitOne را از شی سمافور فراخوانی می کند. این تعداد ترد هایی را که از شی Printer استفاده می کنند، محدود می کند. پس از استفاده از شی چاپگر، هر ترد، متد Release را برای افزایش شمارنده سمافور فراخوانی می کند. این اجازه می دهد تا ترد های بیشتر وارد یک بخش بحرانی شوند.

نظر دهید

آدرس ایمیل شما منتشر نخواهد شد. فیلدهای الزامی علامت گذاری شده اند *