تقريبا تمام اعمال كار با شبكه در Silverlight از مدل asynchronous programming پيروي ميكنند؛ از فراخواني يك متد وب سرويس تا دريافت اطلاعات از وب و غيره. اگر در ساير فناوريهاي موجود در دات نت فريم ورك براي مثال جهت كار با يك وب سرويس هر دو متد همزمان و غيرهمزمان در اختيار برنامه نويس هستند اما اينجا خير. اينجا فقط روشهاي غيرهمزمان مرسوم هستند و بس. خيلي هم خوب. يك چارچوب كاري خوب بايد روش استفادهي صحيح از كتابخانههاي موجود را نيز ترويج كند و اين مورد حداقل در Silverlight اتفاق افتاده است.
براي مثال فراخوانيهاي زير را در نظر بگيريد:
private int n1, n2;
private void FirstCall()
{
Service.GetRandomNumber(10, SecondCall);
}
private void SecondCall(int number)
{
n1 = number;
Service.GetRandomNumber(n1, ThirdCall);
}
private void ThirdCall(int number)
{
n2 = number;
// etc
}
private void FetchNumbers()
{
int n1 = Service.GetRandomNumber(10);
int n2 = Service.GetRandomNumber(n1);
}
private void FetchNumbers()
{
int n1, n2;
Service.GetRandomNumber(10, result =>
{
n1 = result;
Service.GetRandomNumber(n1, secondResult =>
{
n2 = secondResult;
});
});
}
به عبارتي ميخواهيم كل اعمال انجام شده در متد FetchNumbers هنوز Async باشند (ترد اصلي برنامه را قفل نكنند) اما پي در پي انجام شوند تا مديريت آنها سادهتر شوند (هر لحظه دقيقا بدانيم كه كجا هستيم) و همچنين كدهاي توليدي نيز خواناتر باشند.
روش استانداري كه توسط الگوهاي برنامه نويسي براي حل اين مساله پيشنهاد ميشود، استفاده از الگوي coroutines است. توسط اين الگو ميتوان چندين متد Async را در حالت معلق قرار داده و سپس در هر زماني كه نياز به آنها بود عمليات آنها را از سر گرفت.
دات نت فريم ورك حالت ويژهاي از coroutines را توسط Iterators پشتيباني ميكند (از C# 2.0 به بعد) كه در ابتدا نياز است از ديدگاه اين مساله مروري بر آنها داشته باشيم. مثال بعد يك enumerator را به همراه yield return ارائه داده است:
using System;
using System.Collections.Generic;
using System.Threading;
namespace CoroutinesSample
{
class Program
{
static void printAll()
{
foreach (int x in integerList())
{
Console.WriteLine(x);
}
}
static IEnumerable<int> integerList()
{
yield return 1;
Thread.Sleep(1000);
yield return 2;
yield return 3;
}
static void Main()
{
printAll();
}
}
}
كامپايلر سي شارپ در عمل يك state machine را براي پياده سازي اين عمليات به صورت خودكار توليد خواهد كرد:
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = 1;
this.<>1__state = 1;
return true;
case 1:
this.<>1__state = -1;
Thread.Sleep(0x3e8);
this.<>2__current = 2;
this.<>1__state = 2;
return true;
case 2:
this.<>1__state = -1;
this.<>2__current = 3;
this.<>1__state = 3;
return true;
case 3:
this.<>1__state = -1;
break;
}
return false;
}
در حين استفاده از يك IEnumerator ابتدا در وضعيت شيء Current آن قرار خواهيم داشت و تا زمانيكه متد MoveNext آن فراخواني نشود هيچ اتفاق ديگري رخ نخواهد داد. هر بار كه متد MoveNext اين enumerator فرخواني گردد (براي مثال توسط يك حلقهي foreach) اجراي متد integerList ادامه خواهد يافت تا به yield return بعدي برسيم (ساير اعمال تعريف شده در حالت تعليق قرار دارند) و همينطور الي آخر.
از همين قابليت جهت مديريت اعمال Async پي در پي نيز ميتوان استفاده كرد. State machine فوق تا پايان اولين عمليات تعريف شده صبر ميكند تا به yield return برسد. سپس با فراخواني متد MoveNext به عمليات بعدي رهنمون خواهيم شد. به اين صورت ديدگاهي پي در پي از يك سلسه عمليات غيرهمزمان حاصل ميگردد.
خوب ما الان نياز به يك كلاس داريم كه بتواند enumerator ايي از اين دست را به صورت خودكار مرحله به مرحله آن هم پس از پايان واقعي عمليات Async قبلي (يا مرحلهي قبلي)، اجرا كند. قبل از اختراع چرخ بايد متذكر شد كه ديگران اينكار را انجام دادهاند و كتابخانههاي رايگان و يا سورس بازي براي اين منظور موجود است.
ادامه دارد ...