مفاهيم پايه نخها و مشكلات آن
تار و پود يك سيستمعامل كارآمد
نخ1، دنبالهاي از دستورات است كه اجرا ميشود. برنامهاي كه بيشتر از يك دنباله از دستورات را براي اجرا دنبال ميكند، چندنخي2 است. بهعنوان مثال، اگر يك فايل بزرگ را بخواهيم بخوانيم و همزمان، كاربر قادر باشد كه با كليك بر روي گزينه كنسل در هر زماني عمليات را متوقف كند، توسعهدهنده كد با افزودن يك نخ جديد، عمليات ورود داده را بهطور جداگانه انجام ميدهد و با پيادهسازي چند نخ بهجاي يك نخ، برنامه هنگام خواندن فايل، قفل نميكند.
سيستم عامل اين چند نخي بودن را با مكانيزمي بهنام تقسيم زماني3 پياده ميكند. تقسيم زماني همواره رخ ميدهد، حتي اگر از چندين پردازنده در يك سيستم استفاده شود، تعداد نخها بهحدي خواهد رسيد كه باز هم تقسيم زماني رخ خواهد داد. تقسيم زماني در واقع عمل گرفتن كنترل اجراي عمليات از يك نخ (دنبالهاي از دستورات) و انتقال آن به يك نخ ديگر با سرعتي است كه بهنظر برسد هر دو نخها بهطور موازي انجام ميشوند.
ميتوانيم تمام اين روند را به خط فيبرنوري تشبيه كنيم. بهطوري كه فيبرنوري كار پردازشگر را انجام ميدهد و مكالماتي كه ميان ايندو انجام ميشود، نقش نخ را دارد. يك فيبرنوري تكحالته4 ميتواند در لحظه، فقط يك سيگنال را منتقل كند، اما بسياري از مردم ميتوانند با استفاده از همين يك خط، نسبت به برقراري مكالمه همزمان اقدام كنند. اين موضوع بهاين خاطر است كه فيبرنوري بهحد كافي براي تغيير از يك سيگنال به سيگنال ديگر سريع است و بههمين علت مكالمات بدون قطعي ادامه خواهند يافت. نخها هم در يك پروسس چندنخي بههمين ترتيب اجرا خواهند شد.
از آنجايي كه يك نخ معمولا منتظر رويدادهاي مختلفي چون عمليات ورودي™خروجي، ميماند، تغيير از يك نخ به نخ ديگر، باعث اجراي موثرتري ميشود، چرا كه پردازنده بيكار نميماند تا عمليات نخ انجام شود. هر چند، اين انتقال خود باعث ايجاد سرآيندي ميشود كه در صورت وجود نخهاي زياد، سرآيند حاصل نسبت به سود ميزان بيشتري خواهد شد و سيستم كندتر عمل خواهد كرد.
حتي كساني كه با برنامهنويسي آشنايي كمي دارند نيز، به اصطلاح چندنخي برخوردهاند و همواره از پيچيدگي آن چيزهايي را شنيدهاند. اما امروزه، و با وجود بسترهاي نرمافزاري حاضر، سعي شده است كه پيادهسازي چندنخي، تا حد امكان ساده انجام شود و برنامهنويسي چندنخي سادهتر شده است. هر چند كه هنوز پيچيدگيهاي خاصي براي نوشتن يك برنامه با تكنيك چندنخي باقي مانده است و نتيجه اينكه نوشتن يك برنامه كه چندين نخ داشته باشد و از بنبستها جلوگيري كند و احتمال وقوع شرايط رقابتي را بهحداقل برساند، هنوز مهارت خاصي ميطلبد.
اين پيچيدگي با فهميدن اين موضوع كه، بيشتر زبانهاي برنامهنويسي، دستورات تجزيهناپذير ندارند، افزايش مييابد. مثلا دستور++ count، يك دستور ساده در زبان C است، اما در مرحله كامپايل به چندين دستور تبديل ميشود:
1– پردازنده داده موجود در count را ميخواند.
2– پردازنده عدد جديد را محاسبه ميكند.
3– پردازنده مقدار جديد را داخل count ميريزد (كه حتي ممكن است اين ريزدستور هم تجزيهناپذير نباشد.)
پس از اينكه داده مورد دسترسي قرار گرفت و پيش از آنكه مقدار جديد محاسبه شود، نخ ديگري عدد اصلي را تغيير ميدهد و شرايط بحراني براي دسترسي به محتويات متغير count رخ ميدهد و دست آخر، مقدار count غلط محاسبه ميشود.
منبع
Beginning Multithread Applications
2003 Using C#, Wrox,
پينوشتها
Thread.1
Multithread.2
Time Slicing.3
Single Mode.4
تار و پود يك سيستمعامل كارآمد
نخ1، دنبالهاي از دستورات است كه اجرا ميشود. برنامهاي كه بيشتر از يك دنباله از دستورات را براي اجرا دنبال ميكند، چندنخي2 است. بهعنوان مثال، اگر يك فايل بزرگ را بخواهيم بخوانيم و همزمان، كاربر قادر باشد كه با كليك بر روي گزينه كنسل در هر زماني عمليات را متوقف كند، توسعهدهنده كد با افزودن يك نخ جديد، عمليات ورود داده را بهطور جداگانه انجام ميدهد و با پيادهسازي چند نخ بهجاي يك نخ، برنامه هنگام خواندن فايل، قفل نميكند.
سيستم عامل اين چند نخي بودن را با مكانيزمي بهنام تقسيم زماني3 پياده ميكند. تقسيم زماني همواره رخ ميدهد، حتي اگر از چندين پردازنده در يك سيستم استفاده شود، تعداد نخها بهحدي خواهد رسيد كه باز هم تقسيم زماني رخ خواهد داد. تقسيم زماني در واقع عمل گرفتن كنترل اجراي عمليات از يك نخ (دنبالهاي از دستورات) و انتقال آن به يك نخ ديگر با سرعتي است كه بهنظر برسد هر دو نخها بهطور موازي انجام ميشوند.
ميتوانيم تمام اين روند را به خط فيبرنوري تشبيه كنيم. بهطوري كه فيبرنوري كار پردازشگر را انجام ميدهد و مكالماتي كه ميان ايندو انجام ميشود، نقش نخ را دارد. يك فيبرنوري تكحالته4 ميتواند در لحظه، فقط يك سيگنال را منتقل كند، اما بسياري از مردم ميتوانند با استفاده از همين يك خط، نسبت به برقراري مكالمه همزمان اقدام كنند. اين موضوع بهاين خاطر است كه فيبرنوري بهحد كافي براي تغيير از يك سيگنال به سيگنال ديگر سريع است و بههمين علت مكالمات بدون قطعي ادامه خواهند يافت. نخها هم در يك پروسس چندنخي بههمين ترتيب اجرا خواهند شد.
از آنجايي كه يك نخ معمولا منتظر رويدادهاي مختلفي چون عمليات ورودي™خروجي، ميماند، تغيير از يك نخ به نخ ديگر، باعث اجراي موثرتري ميشود، چرا كه پردازنده بيكار نميماند تا عمليات نخ انجام شود. هر چند، اين انتقال خود باعث ايجاد سرآيندي ميشود كه در صورت وجود نخهاي زياد، سرآيند حاصل نسبت به سود ميزان بيشتري خواهد شد و سيستم كندتر عمل خواهد كرد.
حتي كساني كه با برنامهنويسي آشنايي كمي دارند نيز، به اصطلاح چندنخي برخوردهاند و همواره از پيچيدگي آن چيزهايي را شنيدهاند. اما امروزه، و با وجود بسترهاي نرمافزاري حاضر، سعي شده است كه پيادهسازي چندنخي، تا حد امكان ساده انجام شود و برنامهنويسي چندنخي سادهتر شده است. هر چند كه هنوز پيچيدگيهاي خاصي براي نوشتن يك برنامه با تكنيك چندنخي باقي مانده است و نتيجه اينكه نوشتن يك برنامه كه چندين نخ داشته باشد و از بنبستها جلوگيري كند و احتمال وقوع شرايط رقابتي را بهحداقل برساند، هنوز مهارت خاصي ميطلبد.
تجزيه ناپذيري
بياييد كدي را تصور كنيم كه مقداري پول را از يك حساب بانكي منتقل ميكند. در نگاه اول، كد بايستي بررسي كند كه آيا مبلغ كافي در حساب موجود است يا خير؛ اگر مبلغ كافي موجود است، انتقال انجام شود. اگر بعد از بررسي مبلغ موجود در حساب، اجرا به نخي منتقل شود كه مبلغ را از حساب برداشت كند، در اين صورت ممكن است انتقالي انجام شود كه معتبر نيست. مشخص است كه راهحل، كنترل حساب بانكي است، بهگونهاي كه فقط يك نخ بتواند تا پايان عمليات خود به آن دسترسي داشته باشد. عمليات تجزيه ناپذير (Atomic) عملياتي است كه در آن يا تمام مراحل بهطور كامل طي ميشود، يا سيستم به حالت قبل از اجراي آن عمليات برميگردد. انتقال حساب در سيستم بانكي تجزيهناپذير است به اين خاطر كه ممكن است يك نخ ديگر، تجزيهناپذيري عمليات را بهخطر بياندازد. تشخيص و پيادهسازي تجزيهناپذيري يكي از بزرگترين پيچيدگيهاي برنامهنويسي چندنخي است. اين پيچيدگي با فهميدن اين موضوع كه، بيشتر زبانهاي برنامهنويسي، دستورات تجزيهناپذير ندارند، افزايش مييابد. مثلا دستور++ count، يك دستور ساده در زبان C است، اما در مرحله كامپايل به چندين دستور تبديل ميشود:
1– پردازنده داده موجود در count را ميخواند.
2– پردازنده عدد جديد را محاسبه ميكند.
3– پردازنده مقدار جديد را داخل count ميريزد (كه حتي ممكن است اين ريزدستور هم تجزيهناپذير نباشد.)
پس از اينكه داده مورد دسترسي قرار گرفت و پيش از آنكه مقدار جديد محاسبه شود، نخ ديگري عدد اصلي را تغيير ميدهد و شرايط بحراني براي دسترسي به محتويات متغير count رخ ميدهد و دست آخر، مقدار count غلط محاسبه ميشود.
بنبست
براي جلوگيري از چنين موضوعي، زبانهاي برنامهنويسي از قابليتي پشتيباني ميكنند كه قسمتي از كد را، فقط براي تعداد معيني نخ قابل دسترسي ميكند (كه معمولا يك خواهد بود.) هر چند كه اگر ترتيب قفل كردن نخها، صحيح نباشد، امكان وقوع بنبست هم وجود دارد و در اينصورت برنامه هنگ ميكند. عدم قطعيت
مشكل كدي كه تجزيهناپذير نيست و منجر به بنبست ميشود، كاملا به پردازنده و شيوه پيادهسازي چندنخي آن بستگي دارد. نتيجه اين ميشود كه اجراي برنامه، دچار عدم قطعيت خواهد شد و از آنجا كه تشخيص اينكه چطور يك فرآيند چندنخي، بهدرستي يا نادرستي كار ميكند، نيازمند چرخههاي پردازنده و بررسي احتمالات و شانس است، در اين صورت رفع عيب يا پيشگيري از وقوع بنبستها در شرايط آزمايشگاهي در عمل غيرممكن خواهد بود و نيازمند اجرا در زمانهاي طولاني است. منبع
Beginning Multithread Applications
2003 Using C#, Wrox,
پينوشتها
Thread.1
Multithread.2
Time Slicing.3
Single Mode.4