همزمان سازی نخ ها یا (Thread Synchronization)

شنبه 30 آبان 1394

هماهنگ سازی Thread ها ، مکانیزمی است که در آن چندین Thread که به صورت همزمان اجرا می شوند به یک قسمت از برنامه به صورت همزمان دسترسی نداشته باشند.

همزمان سازی نخ ها یا (Thread Synchronization)

هماهنگ سازی Thread ها ، مکانیزمی است که در آن چندین Thread که به صورت همزمان اجرا می شوند به یک قسمت از برنامه به صورت همزمان دسترسی نداشته باشند.

ابتدا پیش زمینه های لازم برای یادگیری بهتر این مقاله را ذکر می کنیم

تعریف Process: Process یک قطعه برنامه در حال اجرا می باشد.یک برنامه مانند یک بازی یا یک سرویس ویا .... می تواند شامل چندین پردازش باشد ولی در اغلب موارد فقط یک پردازش هستند.

Task: فعالیتی است که هم اکنون Cpu در حال انجام ان است و اگر Task Manager را باز کنید لیست پردازش های در حال انجام سیستم را خواهید دید.

Thread:از آنجایی که سرعت انجام پردازش در کامپیوتر بسیار بیشتر از انسان است ، به همین دلیل کامپیوتر قادر به انجام چندین عمل یا Task به صورت همزمان است .پس در واقع هر عملی به چندین عمل کوچک تقسیم شده و هر کدام به عنوان یک Thread در زمان های خالی Cpu اجرا می شوند

از Thread ها برای انجام کارهای موازی و استفاده از وقت تلف شده در بین پردازش ها استفاده می شود.زمانی که یک پردازشی منتظر است که این انتظار می تواند برای عکس العمل کاربر و یا منابع و یا ... باشد در این حالت Thread دیگری که در صف پردازش است انجام می شود و از اتلاف وقت جلوگیری می شود.

بعد از این مقدمات راجع بهThread Synchronization  که موضوع اصلی این مقاله است صحبت می کنیم .

برای اینکه چندین Thread به طور همزمان به یک داده دسترسی داشته باشند باید عمل Synchronization انجام شود.اگر این کار در سیستم انجام نشود Thread ها برای دسترسی به آن داده با هم درگیر شده و حالت Deadlock در سیستم اتفاق می افتد.در حالت Deadlock هیچ کدام از پروسه ها نمی توانند منبع را بگیرند.

روش Synchronization روشی است که در آن تمام Thread  به صورت مدیریت شده و پشت سر هم به داده مشترک دسترسی دارند.وقتی کار یک Thread   با داده یا منبع مشترک تمام شد Thread   بعدی که در صف انتظار است به آن دسترسی پیدا می کند.

مشکلاتی که در روش Thread Synchronization وجود دارند در زیر لیست شده اند.به این معنی که هر برنامه ای که چندین Thread دارد باید مشکلات زیر را رفع کرده باشد

The Producer-Consumer

The Readers-Writers

The Dining Philosopher

به اختصار هر کدام از مشکلات بالا را شرح می دهیم .مشکل Producer-Consumer به این معنی است که دو فرآیند به نام ها مصرف کننده و تولید کننده داریم بین اینها یک بافر به اشتراک گذاشته شده است تولید کننده باید داد ه ای را تولید کرده و در بافر قرار دهد و مصرف کننده باید به طور همزمان آن را به مصرف برساند.در این حالت باید کنترل شود که مصرف کننده در بافر پر داده ای قرار ندهد و مصرف کننده هم سعی نکند از بافر خالی اطلاعات بر دارد.

هر وقت بافر پر بود  و تولید کننده قصد قرار دادن داده را داشت به خواب برود و همین مسئله برای مصرف کننده هم وجود دارد.

مشکل خوانندگان-نویسندگان به این صورت است که تعدادی Thread به یک ناحیه داده به صورت اشتراکی دسترسی دارند ، تعدادی از Thread فقط می خوانند و تعدادی فقط می نویسند.

مشکل بعدی به نام شام فیلسوف ها یا همان Dining Philosopher نام دارد .دراین مشکل تعداد 5 فیلسوف وجود دارند که دور یک میز گرد در حال شام خوردن هستند .بر روی این میز 5 ظرف ماکارونی  و 5 چنگال وجود دارد.فیلسوف ها در حال فکر کردن هستند و یا دارند غذا می خورند تا وقتی هر دو چنگال در دست یک فیلسوف نباشد نمی تواند غذا بخورد .

Thread Synchronization باید قادر به پاسخگویی به مشکلات بالا باشد .البته این روش به تنهایی کاربرد ندارد و مکانیزم های دیگری همراه با آن برای حل مشکلات گفته شده به کار می روند

Thread Synchronization از چهار سناریوی زیر برای رفع مشکل دسترسی به منابع مشترک استفاده می کند

Blocking Methods

Locking Construct

Signaling

No Blocking Synchronization

Blocking Methods

در این تکنیک یک Thread در یک بازه زمانی مشخص و تعریف شده منتظر Thread دیگری می ماند .متدهای مختلفی برای Block شدن وجود دارند از جمله

Sleep

Join

Task.Wait

در روش  Sleep پردازش برای یک مدت زمان تعریف شده ای منتظر می ماند .

در طول این زمان CPU در حالت pause قرار دارد.این تکنیک برای external Task یا وظایف خارجی کاربرد دارد

Thread.Sleep(300)  

Join

این هم یکی از روش های تکنیک Blocking است .شبیه Sleep است ولی تمام Thread ها را Pause نمی کند . Thread فراخوان را Pause می کند تا زمانی که آن Thread که فراخوانی شده اجرایش تمام شود.

class Program    
{    
    static void Main(string[] args)    
    {    
        Thread thread1 = new Thread(Method1);    
        thread1.Start();    
  
        Thread thread2 = new Thread(Method2);    
        thread2.Start();    
  
        thread1.Join();    
        Console.WriteLine("After Thread1");    
  
        thread2.Join();    
        Console.WriteLine("After Thread2");    
        Console.ReadKey();    
  
    }    
  
    private static void Method2(object obj)    
    {    
        Console.WriteLine("Thread1 Executed.");    
    }    
  
    private static void Method1(object obj)    
    {    
        Console.WriteLine("Thread2 Executed");    
    }    
Output:
Thread2 Executed.
Thread1 Executed.
After Thread1
After Thread2 

Task.Wait 

یک متدی از Task blocking است که که به Thread فراخوان تا زمان کامل شدن Thread جاری ، اجازه Wait شدن می دهد .

    class Program    
    {    
        static void Main(string[] args)    
        {    
            Task task = Task.Run(() =>    
            {    
                Random randomNumbers = new Random();    
                long sum = 0;    
                int count = 1000000;    
                for (int i = 1; i <= count; i++)    
                {    
                    int randomNumber = randomNumbers.Next(0, 101);    
                    sum += randomNumber;    
                }    
      
                Console.WriteLine("Total:{0}", sum);    
                Console.WriteLine("Count: {0}", count);    
            });    
            task.Wait();    
                
            Console.ReadKey();    
        }    
    }  

Output:

Total: 50028434

Count: 1000000 

سناریوی دوم Locking  است

Locking  هم اجازه دسترسی کنترل شده به منابع مشترک را می دهد.این سناریو از دو تکنیک زیر استفاده می کند.

Lock

Mutex

روش اول یعنی Lock بخش مشترک را قفل یا Lock می کند .بنابراین در آن واحد تنها یک Thread امکان اجرای این بخش مشترک و یا استفاده از داده های آن را دارد .

     class Program    
     {    
         decimal totalBalance =50000;    
         private Object myLock = new Object();    
      
         static void Main(string[] args)    
         {    
             Program program = new Program();    
             program.WithDraw(5000);          
             Console.ReadKey();    
         }    
      
         public  void WithDraw(decimal amount)    
         {    
             lock (myLock)    
             {    
                 if (amount > totalBalance)    
                 {    
                     Console.WriteLine("Insufficient Amount.");    
                 }    
      
                 totalBalance -= amount;    
                 Console.WriteLine("Total Balance {0}",totalBalance);    
             }    
         }    
      
     }   

Output:

Total Balance 45000

Mutex

یک نوع خاص متغیر است که توسط سیستم عامل پشتیبانی میشود. وظیفه خاص این متغیر حفاظت از ناحیه بحرانی (ناحیه ای که مشترکا توسط چندین Thread باید به آن دسترسی داشت) است .هر نخی که بخواهد از ناحیه مشترک استفاده کند باید Mutex را قفل کند.در این صورت هیج نخ دیگری نمی تواند از داده مشترک استفاده کند.

آموزش سی شارپ

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 4k بازدید
  • 4 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید