بسم الله الرحمن الرحيم
كنا قد تحدثنا فى موضوع ال Delegate انة يمكنك استخدمها خصيصاً للاستفادة من Asynchronous مقدمة عن خصائص ال Delegates
هذا الموضوع يخص كيفية مناداة ال Method بى ال Asynchronous وكيفية ارسال Parameters واستعادة القيم المعادة
Synchronous vs. Asynchronous
للتعرف على فوائد Asynchronous قم اولا بمنادة Method بطيئة الى حد ما
private void button1_Click(object sender, EventArgs e)
{
foo();
}
public void foo()
{
Thread.Sleep(10000);
}عندما ينادى برنامجك هذة الMethod علية الانتظار 10 ثوانى ثم يعود التحكم الى Thread التى نادتها
سنقوم الان بمنادتها Asynchronous ولان عملنا كلة يعتمد على Delegate ومن حسن الحظ دوت نت تعطينا Delegate يمكننا استخدامة مع اى Method لا تعود بقية ولا تأخذ Parameters اسمة MethodeInvoker
private void button2_Click(object sender, EventArgs e)
{
MethodInvoker m = new MethodInvoker(foo);
m.Invoke();
}اعتقد انك لاحظت انة حتى بعد هذة الطريقة ماذا البرنامج ينتظر ال 10ثوانى
Asynchronous method invocation
ماذا لو كنت اريد ان انادى ال Method من دون الانتظار
فى الكود الاتى سنقوم بمناداة ال Method أكثر من مرة من دون انتظار ولن يهمنا متى ستنتهى هذا يسمى Fire and Forget
private void button3_Click(object sender, EventArgs e)
{
MethodInvoker m = new MethodInvoker(foo);
for (int i = 0; i < 100; i++)
{
m.BeginInvoke(null, null);
}
}وللعلم BeginInvoke هى الميزة الايضافية التى منحك إياها ال Delegate وهى الى استخدمت بدلا من Invoke لتحقيق المنادة بدون الانتظار
وبمجرد المنادة asynchronously لن تنفذ فى نفس ال Thread لكن الدوت نت ستخدلها فى ال ThreadPool لانة لو حدثت فى نفس الThrad سندخل فى دوامة الانتظار من جديد
ال ThreadPool لة حدود تصل الى 25 Threads يمكنك التعديل فيها - وبمجرد ان تصل للحد القصى فى وقت التنفيذ ستنتظر ال Method داخل queue حتى يتم تفريغ بعض ال Thread لتدخل هى بعدها (Thread Pool Starvation)
فى الكود التالى عايزين نعرف الباقى جوة ال ThreadPool كام وهل ال Method بتنفذ جواة فعلاً ولا لأ
private void button4_Click(object sender, EventArgs e)
{
MethodInvoker m = new MethodInvoker(Foo2);
for (int i = 0; i < 30; i++)
{
m.BeginInvoke(null, null);
}
}
public void Foo2()
{
int intAvailableThreads, intAvailableIoAsynThreds;
ThreadPool.GetAvailableThreads(out intAvailableThreads,
out intAvailableIoAsynThreds);
string strMessage =
String.Format(@"Is Thread Pool: {0},
Thread Id: {1} Free Threads {2}",
Thread.CurrentThread.IsThreadPoolThread.ToString(),
Thread.CurrentThread.GetHashCode(),
intAvailableThreads);
MessageBox.Show(strMessage);
}لاحظ فى النتيجة ان كل ال Method بتتنفذ فى ال ThreadPool
وكل مناداة جديدة بتعمل ThreadID جديد
ويفضل فى عدد من ال Free Thread لحد 25 مناداة لكن بعد كدة بتستنى ال Method يحصل التحرر او التفريغ الى اتكلمنا عنة
BeginInvoke() and EndInvoke
لاحظنا ان منادة BeginInvoke لا تعطينا فكرة عن متى تنتهى ال Method لكن مع EndInvoke يمكنك تنفيذ مهام اخرى
مع العلم ان EndInvoker تنتظر حتى تستكمل ال Method تنفيذها - ومن هنا نفهم ان مجرد مناداة Begin ثم End فى السطر التالى يعنى انك لم تكسب شىء لذا لا يجب مناداة EndInvoker إلا بعد الانتهاء من تنفيذ ال Method وهو ما يسمى CallBack وسنتحدث عنة هنا
لكن المهم الان هو كيف يتم الربط بيط ال Begin وال End هذا ما يقوم بة ال AsyncResult وهو ال Objectالذى يعود من مناداة Begin وقد تستخدمة أيضاً فى التعامل مع ما يسمى state object
private void button5_Click(object sender, EventArgs e)
{
MethodInvoker m = new MethodInvoker(foo);
IAsyncResult tag = m.BeginInvoke(null, "Passing Some State");
m.EndInvoke(tag);
string s = (string)tag.AsyncState;
MessageBox.Show(s);
}
ماذا لو حدث Exception
سيتم التعامل معة عند EndInvoker
public void FooException()
{
Thread.Sleep(1000);
throw new Exception("Exception from FooException");
}
private void button6_Click(object sender, EventArgs e)
{
MethodInvoker m = new MethodInvoker(FooException);
IAsyncResult tag = m.BeginInvoke(null, "Passing Some State");
try
{
m.EndInvoke(tag);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
string s = (string)tag.AsyncState;
MessageBox.Show(s);
}حاول تنفيذ الكود السابق فى Release Mode
Passing parameters
أصبحت سهلة علينا تنفذ Method لا تأخد وسائط ولكن ماذا عن التى تتعامل مع وسائط Parameters
public void FooParam(int i)
{
i = 20;
}ال Method السابقة تأخذ Param من نوع Int
وال Delegate الخاص بها
public delegate void FooParamDelegate(int i);
فكر فى BeginInvoker و EndInvoker على انة يقسمو ال Method الى اثنان
ال BeginInvoker تأخد كل ال Paramters ثم CallBack ثم StateObject
m.BeginInvoke(i,null,null);
أما بالنسبة ل EndInvoker فهى مسؤلة عن ال Out , Ref Paramters وال Return Value
الجميل فى الموضوع ان compiler قادر على انشاء End وBegin لهم signature مناسبة جداً للى عملتة انت فى تعريف ال Delegate
public void FooParam(int i)
{
i = 20;
}
public delegate void FooParamDelegate(int i);
private void button7_Click(object sender, EventArgs e)
{
int i = 5;
FooParamDelegate m = new FooParamDelegate(FooParam);
IAsyncResult tag= m.BeginInvoke(i,null,null);
m.EndInvoke(tag);
MessageBox.Show(i.ToString());
}لاحظ فى الكود التالى ان ال Out Param هيرجعلنا مع ال EndInvoker
public void FooOutParam(out int i)
{
i = 20;
}
public delegate void FooOutParamDelegate(out int i);
private void button8_Click(object sender, EventArgs e)
{
int i;
FooOutParamDelegate m = new FooOutParamDelegate(FooOutParam);
IAsyncResult tag = m.BeginInvoke(out i,null,null);
m.EndInvoke(out i,tag);
MessageBox.Show(i.ToString());
}والكود الجاى فية In و Out و Return Valueهنستلمها مع ال EndInvoker
public string FooOutInParam(int i,out int iOut)
{
i = 50;
iOut = 50;
return "My Return String";
}
public delegate string FooOutInParamDelegate(int i, out int i2);
private void button9_Click(object sender, EventArgs e)
{
int i=15;
int iOut=15;
FooOutInParamDelegate m = new FooOutInParamDelegate(FooOutInParam);
IAsyncResult tag = m.BeginInvoke(i,out iOut, null, null);
string result= m.EndInvoke(out iOut, tag);
MessageBox.Show("Returned Value Is " + result + " , Out type Is " + iOut.ToString() + " , Value type Still " + i.ToString());
}يبقى فهمنا من الاكود السابقة ان ال Input Parameters تقبل فقط من BeginInvoker لكن ال Out , Ref Parameters بيقبلو مع Begin وEnd علشان نحصل على تحديث القيم
وياللعجب كيف ان ال EndInvoker قادر على نقل ال out ,Ref Param او حتى Exception حتى لو ناديناة من بعد نصف ساعة من Begin وليس هذا فقط بل يحمل بداخلة الكتير كل هذا بفضل ال IAsyncResult العائد من Begin والذى يحتوى على :
- هل اكتملت ال Method
- Reference لل Delegate المستخدم فى مناداة ال Begin
- ال Out Parameters و ال Ref Parameters وال Return Value
- واى Exception يحصل
هكذا علمنا انة لو فقدنا ال AsyncResult سنخسر كل هذة العوائد

Callback delegate
Don’t call me I will call you!
تعرفنا على كيفية ارسال وسائط والتعامل مع Return Value
الان سنتعرف على متى ستنتهى ال Method من التنفيذ
والطريقة هى انك ستعطى ما يسمى CallBack Delegate عند مناداة BeginInvoker بيحث يؤشر على ال Method التى تنفذ بعد اكتمال ال Async Method
public string FooAsyncCallBack(int i, out int iOut)
{
i = 50;
iOut = 50;
return "My Return String";
}
public delegate string FooAsyncCallBackDelegate(int i, out int i2);
private void button10_Click(object sender, EventArgs e)
{
int i=15;
int iOut=15;
FooAsyncCallBackDelegate m = new FooAsyncCallBackDelegate(FooAsyncCallBack);
m.BeginInvoke(i, out iOut, new AsyncCallback(callback), null);
}
public void callback(IAsyncResult ar)
{
int iOut;
AsyncResult result=(AsyncResult)ar;
FooAsyncCallBackDelegate m =(FooAsyncCallBackDelegate)result.AsyncDelegate;
string re= m.EndInvoke(out iOut, result);
MessageBox.Show("Returned Value Is " + result + " , Out type Is " + iOut.ToString());
}اكتشف ما الذى ييحدث داخل ال Call Back Method
تعلمنا انة يجب مناداة EndInvoker لاستعادة ال Out Parameters او ال Return Value
ولعمل ذلك داخل ال Call Back Method يجب اولا تنفيذ الاتى
AsyncResult result=(AsyncResult)ar; FooAsyncCallBackDelegate m =(FooAsyncCallBackDelegate)result.AsyncDelegate;
لكن انتظر السؤال فى أى ThreadId يتم تنفيذ ال Call Back Method
والجواب انة يتم التنفيذ فى نفس ال ThreadId الخاص ب Async Methos وللتأكد من ذلك جرب الكود التالى مع الضغط على الزر اكتر من مرة
public string FooAsyncThreadID(out int iOut)
{
iOut = Thread.CurrentThread.GetHashCode();
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString());
return "My Return String";
}
public delegate string FooAsyncThreadIDDelegate(out int i2);
private void button11_Click(object sender, EventArgs e)
{
int iOut;
FooAsyncThreadIDDelegate m = new FooAsyncThreadIDDelegate(FooAsyncThreadID);
m.BeginInvoke(out iOut, new AsyncCallback(callbackThreadID), null);
}
public void callbackThreadID(IAsyncResult ar)
{
int iOut;
AsyncResult result = (AsyncResult)ar;
FooAsyncThreadIDDelegate m = (FooAsyncThreadIDDelegate)result.AsyncDelegate;
string re = m.EndInvoke(out iOut, result);
MessageBox.Show("My Foo Thread ID is " + iOut + " , My CallBack Thread ID Is " + Thread.CurrentThread.GetHashCode().ToString() );
}ياسلام لو شغلت ال Comment
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString());
هتلاقى العجب ! ال Method التى تخلصها الاول هيترد عليا فى نفس ال ThreadID بتاعها
تخيل لو الدوت نت هيعملك ال Async Method فى Thread و ال Call Back Method فى Thread تانية يبقى هندخل فى دوامة ال (Thread Pool Starvation)

