في نافذة الطباعة اختر «حفظ كملف PDF» كوجهة.
نظم التشغيل 1، الدرس العملي 5
خيط رئيسي واحد، ثم خيوط كثيرة: أنشئ الخيوط وابدأها، ومرر إليها البيانات، وانتظرها، وراقب كل حالة
ThreadParameterizedThreadStart، ثم بطريقة آمنة من حيث النوع (type-safe)؛ واسترجاع النتيجة عبر رد النداء (callback)ThreadState عبر Start وSleep وJoinMainالجزء 1
من الذي يشغل شيفرتك فعلا؟
تعدد المهام (multitasking): متصفح وVisual Studio والمفكرة مفتوحة في الوقت نفسه
نظام التشغيل
يشغل تطبيقات كثيرة
العملية
واحدة لكل تطبيق، ولها PID خاص بها
الخيط
ينفذ الشيفرة
MainSystem.ThreadingCurrentThread خاصية ساكنة (static)؛ أما Name فتخص كائن الخيط. توقع السطرين الأولين.
Thread t = Thread.CurrentThread;
Console.WriteLine("Name before: [" + t.Name + "]");
Console.WriteLine("Name is null: " + (t.Name == null));
t.Name = "Main Thread";
Console.WriteLine("t.Name: " + t.Name);
Console.WriteLine("Thread.CurrentThread.Name: " + Thread.CurrentThread.Name);Name before: []
Name is null: True
t.Name: Main Thread
Thread.CurrentThread.Name: Main Threadالجزء 2
كم يكلف الانتظار، وكيف تتجنبه الخيوط
تستدعي Main الدوال Method1() وMethod2() وMethod3() وتقيس زمنها. توقع: متى تبدأ Method3؟ وكم تستغرق Main؟
static void Method2()
{
for (int i = 1; i <= 3; i++)
{
if (i == 2)
{
Console.WriteLine("Method2: database operation started");
Thread.Sleep(5000); // pretend this is a slow DB call
Console.WriteLine("Method2: database operation completed");
}
Console.WriteLine("Method2: " + i);
}
}Main started
Method1: 1
Method1: 2
Method1: 3
Method2: 1
Method2: database operation started
Method2: database operation completed
Method2: 2
Method2: 3
Method3: 1
Method3: 2
Method3: 3
Main ended after 5.0 sMethod3 مدة 5 s من أجل استدعاء قاعدة بيانات لا تحتاج إليهالحل: أعط كل دالة خيطا خاصا بها. توقع: هل الترتيب نفسه في كل تشغيل؟ ومتى تنتهي Main؟
Stopwatch sw = Stopwatch.StartNew();
Thread.CurrentThread.Name = "Main";
Console.WriteLine("Main started");
Thread t1 = new Thread(Method1) { Name = "Thread1" };
Thread t2 = new Thread(Method2) { Name = "Thread2" };
Thread t3 = new Thread(Method3) { Name = "Thread3" };
t1.Start();
t2.Start();
t3.Start();
Console.WriteLine("Main ended after " + sw.Elapsed.TotalSeconds.ToString("0.0") + " s");الترتيب الذي طبعت به الحلقات أرقامها، في ثلاث مرات تشغيل حقيقية
| التشغيل | ترتيب أسطر الحلقات | انتهت Main بعد |
|---|---|---|
| 1 | Method1 1-3, Method2 1, Method3 1-3, Method2 2-3 | 0.1 s |
| 2 | Method1 1, Method2 1, Method1 2-3, Method3 1-3, Method2 2-3 | 0.0 s |
| 3 | Method3 1-3, Method1 1-3, Method2 1, Method2 2-3 | 0.1 s |
يحصل كل خيط على شريحة زمنية (time slice) قصيرة؛ والخيط الذي لم ينته ينتظر دوره التالي
t1
شريحة
t2
شريحة
t3
شريحة
t1
مرة أخرى
t2
مرة أخرى
الخيط الرئيسي
تنشئه العملية، ويبدأ من Main
t1
خيط عامل
t2
خيط عامل
t3
خيط عامل
الجزء 3
نقاط الدخول، والبيانات الداخلة، والنتائج الخارجة
توقع: هل تتم ترجمة هذه الشيفرة؟
class MyThread : Thread
{
}Program.cs(4,18): error CS0509: 'MyThread': cannot derive from sealed type 'Thread'| العضو | المعنى |
|---|---|
Thread.CurrentThread | ساكنة: الخيط الذي ينفذ هذا السطر |
Name | قراءة الاسم أو تعيينه (null افتراضيا) |
ThreadState | الحالة الحالية للخيط |
IsAlive | True من Start حتى ينتهي الخيط |
IsBackground | قراءة أو تعيين: خيط خلفي أو أمامي |
يحتاج الخيط إلى نقطة دخول (entry point): مؤشر إلى الدالة التي يبدأ منها، يمرر إلى المنشئ (constructor) في صورة مفوض (delegate)
| المفوض | يطابق | طريقة البدء |
|---|---|---|
ThreadStart | void M() | t.Start() |
ParameterizedThreadStart | void M(object x) | t.Start(value) |
void: دالة الخيط لا تستطيع إرجاع قيمة مباشرةobjectتطبع DisplayNumbers() اسم خيطها و1 2 3. توقع المخرجات.
Thread t1 = new Thread(DisplayNumbers) { Name = "t1" };
ThreadStart ts = new ThreadStart(DisplayNumbers);
Thread t2 = new Thread(ts) { Name = "t2" };
Thread t3 = new Thread(new ThreadStart(DisplayNumbers)) { Name = "t3" };
Thread t4 = new Thread(delegate () { DisplayNumbers(); }) { Name = "t4" };
Thread t5 = new Thread(() => DisplayNumbers()) { Name = "t5" };Run 1 Run 2 Run 3
t1: 1 2 3 t1: 1 2 3 t2: 1 2 3
t4: 1 2 3 t2: 1 2 3 t4: 1 2 3
t2: 1 2 3 t3: 1 2 3 t3: 1 2 3
t5: 1 2 3 t5: 1 2 3 t1: 1 2 3
t3: 1 2 3 t4: 1 2 3 t5: 1 2 3توقع: هل تتم ترجمة new Thread(Sum)؟
Thread t1 = new Thread(Sum);
t1.Start();
static int Sum()
{
int result = 0;
for (int i = 1; i <= 5; i++)
result += i;
return result;
}تأخذ الدالة معاملا واحدا من النوع object؛ وتمرر القيمة إلى Start
Program obj = new Program();
Thread t1 = new Thread(obj.DisplayNumbers);
t1.Start(5);
public void DisplayNumbers(object max)
{
int number = Convert.ToInt32(max);
for (int i = 1; i <= number; i++)
Console.WriteLine("Number: " + i);
}Number: 1
Number: 2
Number: 3
Number: 4
Number: 5Start(5) القيمة 5 إلى المفوض، فيمررها المفوض إلى maxConvert.ToInt32 القيمة من object إلى intغير سطرا واحدا إلى t1.Start("hi"); توقع: خطأ في الترجمة، أم شيء آخر؟
object يقبل أي قيمةclass NumberHelper
{
private int _number;
public NumberHelper(int number)
{
_number = number;
}
public void DisplayNumbers()
{
for (int i = 1; i <= _number; i++)
Console.WriteLine("Number: " + i);
}
}ThreadStartnew Thread(obj.DisplayNumbers) بعد obj = new NumberHelper(5)الخطأ نفسه كما سبق، لكن مع الصنف المساعد هذه المرة. توقع: متى يكتشف؟
NumberHelper obj = new NumberHelper("hi");
Thread t1 = new Thread(obj.DisplayNumbers);
t1.Start();Program.cs(24,45): error CS1503: Argument 1: cannot convert from 'string' to 'int'public delegate void ResultCallback(int result);
public void CalculateSum()
{
int result = 0;
for (int i = 1; i <= _number; i++)
{
Console.WriteLine(i);
result = result + i;
}
if (_callback != null)
_callback(result);
}توقع السطر الأخير (نعرض من المخرجات آخر 3 أسطر من أصل 11). S = المجموع.
ResultCallback callback = new ResultCallback(ShowResult);
NumberHelper obj = new NumberHelper(10, callback);
Thread t1 = new Thread(obj.CalculateSum);
t1.Start();
static void ShowResult(int result)
{
Console.WriteLine("Sum of the numbers is: " + result);
}9
10
Sum of the numbers is: 55الجزء 4
الحالات، وSleep، وJoin، والخيوط الأمامية والخلفية
| حالة دورة الحياة | ThreadState | متى |
|---|---|---|
| غير مبدوء | Unstarted | أنشئ، ولم تستدع Start بعد |
| جاهز / يعمل | Running | بدأ وغير محجوب (blocked) |
| غير قابل للتشغيل | WaitSleepJoin | محجوب في Sleep أو Join، أو ينتظر |
| منته | Stopped | انتهت دالته |
Unstarted
Running
WaitSleepJoin
Running
مرة أخرى
Stopped
توقع الأسطر من 1 إلى 5 قبل الكشف عنها
Thread t = new Thread(Work);
Console.WriteLine("1) after new: " + t.ThreadState + ", IsAlive = " + t.IsAlive);
t.Start();
Thread.Sleep(500); // give the worker time to reach its Sleep
Console.WriteLine("3) while it sleeps: " + t.ThreadState + ", IsAlive = " + t.IsAlive);
t.Join();
Console.WriteLine("5) after Join: " + t.ThreadState + ", IsAlive = " + t.IsAlive);
static void Work()
{
Console.WriteLine("2) inside Work: " + Thread.CurrentThread.ThreadState);
Thread.Sleep(2000);
Console.WriteLine("4) Work is about to end");
}1) after new: Unstarted, IsAlive = False
2) inside Work: Running
3) while it sleeps: WaitSleepJoin, IsAlive = True
4) Work is about to end
5) after Join: Stopped, IsAlive = FalseRunningMain مدة 0.5 s، لذا فالخيط العامل داخل Sleep الذي مدته 2 sJoin إلا بعد انتهاء Workتكرر ProcessSleep الحلقة من i = 0 إلى 5 وتستدعي Thread.Sleep(4000) في كل دورة؛ وتبدؤها Main وتنتظرها بالدالة Join ثم تطبع الزمن. توقع TotalTime.
static void ProcessSleep()
{
for (int i = 0; i <= 5; i++)
{
Console.WriteLine("work is in progress..!");
Thread.Sleep(4000); // sleep for 4 seconds
}
}TotalTime 00:00:24
work completed..!يجعل t.Join() الخيط المستدعي ينتظر حتى ينتهي t. توقع المخرجات بدون السطر Join.
Thread oThread = new Thread(MethodJoin); // prints "work is in progress..!" 3 times
oThread.Start();
oThread.Join();
Console.WriteLine("work completed..!");| مع Join | بدون Join (في 6 من 6 مرات) |
|---|---|
work is in progress..! | work completed..! |
work is in progress..! | work is in progress..! |
work is in progress..! | work is in progress..! |
work completed..! | work is in progress..! |
| أمامي (الافتراضي) | خلفي | |
|---|---|---|
IsBackground | False | True |
| ينتهي الخيط الرئيسي أولا | يستمر في العمل حتى النهاية | يوقف مع آخر خيط أمامي |
| يبقي البرنامج حيا | نعم | لا |
توقع المخرجات، وكم من الوقت يستمر البرنامج
Thread oThread = new Thread(WorkThread);
oThread.Start();
Console.WriteLine("Main Thread Quits..!");
static void WorkThread()
{
for (int i = 0; i <= 4; i++)
{
Console.WriteLine("Worker Thread is in progress..!");
Thread.Sleep(2000); // sleep for 2 seconds
}
Console.WriteLine("Worker Thread Quits..!");
}Worker Thread is in progress..!
Main Thread Quits..!
Worker Thread is in progress..!
Worker Thread is in progress..!
Worker Thread is in progress..!
Worker Thread is in progress..!
Worker Thread Quits..!Main فوراأضف سطرا واحدا قبل Start. توقع المخرجات.
Thread oThread = new Thread(WorkThread);
oThread.IsBackground = true;
oThread.Start();
Console.WriteLine("Main Thread Quits..!");Main Thread Quits..!
Worker Thread is in progress..!نظرة إلى الأمام
يشغل System.Threading.Tasks.Task إجراء (action) على خيط منفصل نيابة عنك. توقع: أي سطر يأتي مباشرة بعد Main thread started؟
Console.WriteLine("Main thread started");
Task task = new Task(() =>
{
Console.WriteLine("Running task in separate thread");
int result = AddNumbers(5, 10);
Console.WriteLine("Result of addition: " + result);
});
task.Start();
Console.WriteLine("Main thread is done");
Console.ReadLine();Main thread started
Main thread is done
Running task in separate thread
Result of addition: 15الجزء 5
نحو 30 دقيقة: توقع على الورق، ثم شغل البرنامج وتحقق
نحو 5 دقائق: توقع كل سطر، بالترتيب
Thread t = new Thread(Work) { Name = "Worker" };
Console.WriteLine("A: " + t.ThreadState);
Console.WriteLine("B: " + t.IsBackground);
t.Start();
Thread.Sleep(1000);
Console.WriteLine("C: " + t.ThreadState + " " + t.IsAlive);
t.Join();
Console.WriteLine("D: " + t.ThreadState + " " + t.IsAlive);
static void Work()
{
Thread me = Thread.CurrentThread;
Console.WriteLine("W: " + me.Name + " " + me.ThreadState);
Thread.Sleep(3000);
}A: Unstarted
B: False
W: Worker Running
C: WaitSleepJoin True
D: Stopped FalseRunningSleep الذي مدته 3 sنحو 6 دقائق
توقع الزمنين. وماذا يحدث بدون استدعاءات Join الثلاثة؟
Stopwatch sw = Stopwatch.StartNew();
Download(); Resize(); Save();
Console.WriteLine("One thread: " + sw.Elapsed.TotalSeconds.ToString("0.0") + " s");
sw.Restart();
Thread t1 = new Thread(Download);
Thread t2 = new Thread(Resize);
Thread t3 = new Thread(Save);
t1.Start(); t2.Start(); t3.Start();
t1.Join(); t2.Join(); t3.Join();
Console.WriteLine("Three threads: " + sw.Elapsed.TotalSeconds.ToString("0.0") + " s");
static void Download() { Thread.Sleep(2000); }
static void Resize() { Thread.Sleep(3000); }
static void Save() { Thread.Sleep(1000); }One thread: 6.0 s
Three threads: 3.0 sنحو 8 دقائق
FactorialHelper: يحسب المضروب (factorial) n!، أي 1 x 2 x ... x n، على خيط منفصلn بطريقة آمنة من حيث النوع5! = 120Main احسب 5! ثم 10!، كل منهما على خيط خاص به؛ ويجب أن يطبع 5! أولاnew FactorialHelper("5", ShowResult)؟مع public delegate void FactorialCallback(int n, long result);
public FactorialHelper(int n, FactorialCallback callback)
{ _n = n; _callback = callback; }
public void Calculate()
{
long result = 1;
for (int i = 2; i <= _n; i++)
result = result * i;
if (_callback != null)
_callback(_n, result);
}5! = 120
10! = 3628800t.Join() بعد الخيط الأول 5! في المقدمة"5": الخطأ CS1503، لا يمكن التحويل من 'string' إلى 'int'نحو 5 دقائق
توقع المخرجات كما هو مكتوب، ثم مع worker.IsBackground = true; قبل Start
Thread worker = new Thread(Tick);
worker.Start();
Thread.Sleep(1500);
Console.WriteLine("Main done");
static void Tick()
{
for (int i = 1; i <= 3; i++)
{
Console.WriteLine("tick " + i);
Thread.Sleep(1000);
}
Console.WriteLine("Worker done");
}كل إصدار: المخرجات نفسها في 10 مرات من أصل 10
| أمامي (كما هو مكتوب) | خلفي |
|---|---|
tick 1 | tick 1 |
tick 2 | tick 2 |
Main done | Main done |
tick 3 | (لا شيء بعد ذلك) |
Worker done |
نحو 6 دقائق
Thread t = new Thread(PrintSquares);
t.Start(4);
t.Join();
Console.WriteLine("Main done");
static void PrintSquares(object max)
{
int n = Convert.ToInt32(max);
for (int i = 1; i <= n; i++)
Console.WriteLine(i + " x " + i + " = " + (i * i));
}t.Start("four")؟void A() وvoid B(object x) وvoid C(int x) وint D()؟1 x 1 = 1
2 x 2 = 4
3 x 3 = 9
4 x 4 = 16
Main done| الدالة | new Thread(M) |
|---|---|
void A() | ThreadStart |
void B(object x) | ParameterizedThreadStart |
void C(int x) | الخطأ CS1503 |
int D() | الخطأ CS1503 |
new Thread(method) + Start(): تعمل الدوال جنبا إلى جنب، بترتيب لا يمكن توقعهThreadStart أو ParameterizedThreadStart: ترجع void، بلا معامل أو بمعامل واحد من النوع objectSleep وJoin الخيط في الحالة WaitSleepJoin، والخيط المنتهي يكون StoppedMahmoud Abas|أساسيات الخيوط بلغة C#