چند نخی در C#

چند نخی در C#

احتمال خیلی زیاد تا به حال با کلمه چندنخی یا Multithreading در C# یا در زبان های برنامه نویسی دیگر مواجه شدید.

زمانی که در حال یادگیری این مفهوم از طریق مقالات و مستندات بودم اکثر مقالات نمی تونستند تفاوتی که چند نخی ایجاد میکنه رو توضیح بدن. در واقع بعضی از نویسندگان طوری توضیح داده بودن که تفاوتی با asynchronous نداشت.  من جایی توضیحی پیدا نکردم که تفاوت های Parallel Programming, multithreading, asynchronous رو توضیح بده و این ها باعث گیج شدن ما میشن.

در این مقاله تلاش دارم به روش ساده ای این ها رو توضیح بدم. چند نخی چیه، چرا ازش استفاده میکنیم و در چه موقعیت هایی کاربردی است. همچنین آیا با asynchronous متفاوت است یا خیر، و در نهایت مثال هایی از ساختار چندنخی نوشته میشه.

Thread چیست؟

در ویرایشگر word بخشی که چند دقیقه یک بار مقاله رو به طور خودکار ذخیره می کنه یک thread است (یعنی thread روشی برای اجرای وظیفه است)

Muti Thread چیست؟

در زمانی که ویرایشگر word در حال ذخیره سازی سند است، Gramerly در پشت صحنه در حال کار است تا اشتباهات املایی جملات را پیدا کند، به اجرای همزمان دو وظیفه چند نخی گفته می شود (داشتن بیش از یک اجرا)

MultiThreading چیست؟

استفاده از چند نخ با اجرای همزمان که روی انجام وظایف مختلف کار میکنند را چند نخی می گوییم.

کاربرد چند نخی چیست؟

نخ با انجام کارها به صورت برنامه نویسی موازی در بهبود کارایی برنامه به ما کمک می کند.

برنامه نویسی موازی چیست؟

جریان برنامه است که برنامه به بخش هایی شکسته شده و برای هر بخش CPU اختصاص می یابد که به طور همزمان به اجرای وظایف بپردازد. لذا، با تقسیم بندی این بخش ها زمان اختصاص یافته به هر برنامه کاهش می یابد و باعث افزایش کارایی برنامه خواهد شد.

آیا برنامه نویسی موازی و Asynchronous یکی هستند؟

Asynchronous یک روش انجام کارها به صورت non-blocking است، یعنی بدون انتظار برای یک event اجرا می شود و می شه به اجرای بقیه کارها ادامه داد.

تفاوت این است که Parallel وابسته به سخت افزار است ولی asynchronous در رابطه با Task ها است و چند نخی در رابطه با worker ها است.

نکته

C# از اجرای موازی کد با کمک چند نخی پشتیبانی می ند (یعنی چند نخی روش موازی اجرا است)

ساختار چند نخی

کلمه کلیدی Thread یک کلاس در فضای نامی System.Threading است.

Thread چگونه ایجاد می شود؟

برای پیاده سازی Thread نیاز داریم از فضای نامی threading در برنامه استفاده کنیم و کلاس Thread داخل این فضای نامی است، می توانیم یک شی Thread ایجاد کنیم و این اشیا thread در برنامه thread ایجاد می کنند.

Tread چطور کار می کند؟

در چند نخی، زمان بند thread از زمان بند سیستم عامل برای زمان بندی نخ ها کمک می گیرد تا بتواند به هر thread زمان اختصاص دهد.

در یک ماشین تک پردازشگر، فقط یک thread در آن واحد اجرا می شود، برای یک پردازشگر دو هسته ای می توانیم 4 thread داشته باشیم و برای یک پردازشگر quad-core میتونیم 8 thread داشته باشیم. مدیریت نامناسب ممکن است روی یک ماشین تک پردازشگر چندین thread ایجاد کند و منجر به گلوگاه (bottleneck) منابع شود.

مثالی از چند نخی

using System;  
using System.Threading;  
class Program {  
    public static void Main() {  
        Thread ThreadObject1 = new Thread(Example1); //Creating the Thread    
        Thread ThreadObject2 = new Thread(Example2);  
        ThreadObject1.Start(); //Starting the Thread    
        ThreadObject2.Start();  
    }  
    static void Example1() {  
        Console.WriteLine("Thread1 Started");  
        for (int i = 0; i <= 5; i++) {  
            Console.WriteLine("Thread1 Executing");  
            Thread.Sleep(5000); //Sleep is used to pause a thread and 5000 is MilliSeconds that means 5 Seconds    
        }  
    }  
    static void Example2() {  
        Console.WriteLine("Thread2 Started");  
        for (int i = 0; i <= 5; i++) {  
            Console.WriteLine("Thread2 Executing");  
            Thread.Sleep(5000);  
        }  
    }  
}

در مثال فوق بین MainThread و Worker Thread تمایز ایجاد کردیم

MainThread چیست؟

Thread ی که در یک پردازش اول اجرا می شود.

پردازش یا Process در چندنخی به چه معنی است؟

برای اجرای برنامه به منابع ارائه شده توسط این پردازش نیاز داریم. هر پردازش با یک thread واحد به نام primary thread شروع می شود. این پردازش قادر است تا thread های اضافی از thread که در اختیار دارد ایجاد کند.

Synchronization چیست؟

اجرای همروند دو تا چند thread.

چرا نیاز به synchronization داریم؟

چند نخی از تداخل thread ها جلوگیری می کند، این تداخل ها زمانی رخ می دهد که به طور موازی تلاش دارند متغیرهایی را تغییر دهند. Synchronous تضمین می کند که فقط یک پردازش یا thread به بخش حیاتی برنامه دسترسی دارد.

چطور synchronization را مدیریت کنیم؟

برای هندل این موضوع ما چندین روش داریم که به ۴ دسته تقسیم می شوند:

Blocking Methods

Locking Constructs

No blocking Synchronization

Signalling

Join

در thread حالت synchronization join مکانیزم blocking است که به توقف فراخوانی thread کمک می کند. تا زمانی که اجرای thread ی که join روی آن فراخوانی شده است اجرای متوقف می شود.

تفاوت استفاده از join و عدم استفاده از آن در مثال زیر توضیح داده شده.

using System;  
using System.Threading;  
class Program {  
    public static void Main() {  
        //Creating the WorkerThread with the help of Thread class.    
        Thread ThreadObject1 = new Thread(WorkerThread);  
        ThreadObject1.Start(); //Starting the Thread    
        //ThreadObject1.Join(); //Using Join to block the current Thread    
        Console.WriteLine("1. MainThread Started");  
        for (int i = 0; i <= 3; i++) {  
            Console.WriteLine("-> MainThread Executing");  
            Thread.Sleep(3000); //Here 5000 is 5000 Milli Seconds means 5 Seconds    
        }  
        // We are calling the Name of Current running Thread using CurrentThread    
        Thread Th = Thread.CurrentThread;  
        Th.Name = "Main Thread";  
        Console.WriteLine("\nGetting the Name of Currently running Thread");  
        //Name Property is used to get the name of the current Thread    
        Console.WriteLine($ "Current Thread Name is: {Th.Name}");  
        //Priority Property is used to display the Priority of current Thread    
        Console.WriteLine($ "Current Thread Priority is: {Th.Priority}");  
    }  
    static void WorkerThread() {  
        Console.WriteLine("2. WorkerThread Started");  
        for (int i = 0; i <= 3; i++) {  
            Console.WriteLine("-> WorkerThread Executing");  
            Console.WriteLine("Child Thread Paused");  
            //Sleep method is used to pause the Thread for a specific period    
            Thread.Sleep(3000);  
            Console.WriteLine("Child Thread Resumed");  
        }  
    }  
}  

خروجی با استفاده از join

خروجی بدون استفاده از join

Lock

Lock به قفل کردن thread جاری کمک می کند به طوریکه thread دیگری نمی تواند در اجرا وقفه ای ایجاد کند. باز کردن قفل بعد از پایان اجرا رخ می دهد.

کد مثال زیر lock synchronization method را توضیح می دهد

using System;  
using System.Threading;  
namespace LockDemo {  
    class LockExample1 {  
        //Creating a normal Method to Display Names    
        public void Display() {  
            //Lock is used to lock-in the Current Thread    
            lock(this) {  
                for (int i = 0; i <= 3; i++) {  
                    Thread.Sleep(3000);  
                    Console.WriteLine($ "My Name is Abhishek{i}");  
                }  
            }  
        }  
    }  
    class Example2 {  
        public static void Main(string[] args) {  
            //Creating object for LockExample1 Class as _locker so that we can access its Display Method    
            LockExample1 _locker = new LockExample1();  
            Console.WriteLine("Threading with the help of Lock");  
            //Calling the Display Method using ThreadStart Delegate which is supplied to Thread constructor.    
            Thread t1 = new Thread(new ThreadStart(_locker.Display));  
            Thread t2 = new Thread(new ThreadStart(_locker.Display));  
            t1.Start(); //Starting Thread1    
            t2.Start(); //Starting Thread2    
        }  
    }  
}  

 

خروجی- Threading بهمراه Lock

خروجی - Threading بدون Lock

Deadlocks

در Deadlock دو thread منتظر resource ی هستند که نیاز دارند اما این resource توسط دیگری نگه داشته شده و باعث می شود هیچ یک از آنها نتوانند به کار ادامه بدن. Lock1 منابع Lock2 رو داره و Lock2 منابع Lock1 رو داره و سردرگمی پیش میارن، لذا هیچ راهی به جز متوقف کردن این وضعیت deadlock نداریم.

برنامه مثال برای Deadlock

using System;  
using System.Threading;  
namespace DeadlocksDemo {  
    public class Locking {  
        static readonly object Lock1 = new object();  
        static readonly object Lock2 = new object();  
        static void Display() {  
            Console.WriteLine("Trying to lock Locker1");  
            lock(Lock1) {  
                Console.WriteLine("Locked Locker1");  
                Thread.Sleep(1000);  
                Console.WriteLine("Locking Locker2");  
                lock(Lock2) {  
                    Console.WriteLine("Locked Locker2");  
                }  
                Console.WriteLine("Released Locker2");  
            }  
            Console.WriteLine("Released Locker1");  
        }  
        static void Main() {  
            new Thread(new ThreadStart(Display)).Start();  
            Thread.Sleep(1000);  
            Console.WriteLine("Trying to lock Locker2");  
            lock(Lock2) {  
                Console.WriteLine("Locked Locker2");  
                Console.WriteLine("Locking Locker1");  
                lock(Lock1) {  
                    Console.WriteLine("Locked Locker1");  
                }  
                Console.WriteLine("Released Locker1");  
            }  
            Console.WriteLine("Released Locker2");  
            Console.Read();  
        }  
    }  
}  

خروجی

منبع:

https://www.c-sharpcorner.com/article/multithreading-in-c-sharp/

 

اشتراک گذاری