الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

أساسيات Asynchronous

مغلق
بدأه a_elbadrawy في 23 يونيو 2007 · 6 رد · 1,843 مشاهدة · في Microsoft Visual C#.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

بسم الله الرحمن الرحيم

كنا قد تحدثنا فى موضوع ال 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 سنخسر كل هذة العوائد

mainqx2.gif

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)

threadidjq7.gif

Asynchronous.rar

تم تعديل هذه المشاركة بواسطة bedoonline في 23 يونيو 2007 في 19:53

Practice makes perfect 2007*

البرمجة بلا انطباعات

#3

الله يعطيك العافية

موضوع شيق وكنت محتاج له . وما تركت سطر إلا وقريته وراح أبدأ أطبقه إن شاء الله قريباً .

تقبل تحياتي B)

#4

السلام عليكم

شكرا أخي على هذا الموضوع الجيد ولي ملاحظتين لو تسمح

1. في الدالة callback إستعمل

MessageBox.Show("Returned Value Is " + re + " , Out type Is " + iOut.ToString());

عوض

MessageBox.Show("Returned Value Is " + result + " , Out type Is " + iOut.ToString());

2. تتعلق ب"التنظيف" إثر إستعمالInvoke, لم تضف ذلك ;)

#5
Ben_Laiid كتب:
السلام عليكم

شكرا أخي على هذا الموضوع الجيد ولي ملاحظتين لو تسمح

1. في الدالة callback إستعمل

MessageBox.Show("Returned Value Is " + re + " , Out type Is " + iOut.ToString());

عوض

MessageBox.Show("Returned Value Is " + result + " , Out type Is " + iOut.ToString());

2. تتعلق ب"التنظيف" إثر إستعمالInvoke, لم تضف ذلك ;)

ههههههههه جل من لا يسهو ياأخى Ben_Laiid

أما بالنسبة للطلب التانى والله الموضوع طول اوى بصراحة وعلى العموم المرات الجاية ان شاء الله تبقى المواضيع ملمومة شوية عن كدة ;) ;)

Practice makes perfect 2007*

البرمجة بلا انطباعات

#6

نشكرك يا أخ bedo على هذا الموضع الرائع, في الحقيقة لم أقرأه كله للانشغال. إنما عموما هو متميز وان شاء الله أضعه ضمن المواضيع المتميزه في القسم.

#Read

http://MoutazShams.blogspot.com

projects003.gif

أعتذر عن التغيب هذه الأيام نظرا للانشغال المريب

#7
معتز شمس كتب:
نشكرك يا أخ bedo على هذا الموضع الرائع, في الحقيقة لم أقرأه كله للانشغال. إنما عموما هو متميز وان شاء الله أضعه ضمن المواضيع المتميزه في القسم.

جزاك لله خيراً أخى معتز

Practice makes perfect 2007*

البرمجة بلا انطباعات

هذا الموضوع مغلق.

مواضيع مشابهة