» נושאי לימוד
» נושאי לימוד
יום שני 29 באפריל 2024
סקירה כללית
דף ראשי  מתקדמים  Treads of control  סקירה כללית גרסה להדפסה

סקירה כללית

 

  thread Aהוא קיצור של thread of control . נלמד גם מונחים נוספים בהמשך הסקירה. 

אם אי פעם עבדת עם דפדפן רשת, השתמשת ב- threads מבלי שאפילו היית מודע לכך.

דפדפני הרשת משתמשים ב- threads שונים הפועלים במקביל לביצוע מגוון משימות, לכאורה בו זמנית.

לדוגמא בדפי אינטרנט רבים, אתה יכול לגלול באמצעות העכבר ולקרוא את הטקסט, לפני שכל הסימנים מופיעים על המסך.

במקרה זה הדפדפן מוריד סימן ב thread אחד ותומך בפעולת הגלילה שלך ב- thread אחר.

 

 C  ו- ++C לא תומכות בפעולת ה- threading כפעולה המוטמעת בשפה. אם אתה צריך לכתוב תוכנית עם threads באחת השפות האלה, עליך לטפל במאפיינים של מערכת ההפעלה או אולי להרכיב סדרה של ספריות מחלקה הבנויות במיוחד כדי לתמוך ב- threading עבור פלטפורמה מסוימת.

 

ג'אווה תומכת ב- threading ישירות, כך שהיא מאפשרת לכתוב תוכניות threads בלתי תלויות בפלטפורמה (לצערנו, עם מס' הבדלים בהתנהגות של הפלטפורמות השונות).


ההבדל בין   Multiprocessingל- Multithreading

 

רוב הספרים מקדישים עמוד אחד או יותר לנושא זה. דיון מעניין, ומשעשע במידה מסוימת, בנושא אפשר למצוא בספר Java Primer Plus  מאת Tyma, Torok ו-Downing.
בקצרה ניתן לומר כי
multiprocessing  מתייחס לשתי תוכניות או יותר הפועלות "לכאורה" במקביל, תחת בקרה של מערכת ההפעלה. לא חייב להיות שום קשר בין התוכניות מלבד העובדה שאתה רוצה להתחיל ולהריץ את כולן בו זמנית.

          

 ל- multiprocessing מתייחסים לפעמים כprocess  "כבד", כנראה מכיוון שהאתגרים המעורבים בהפעלתו הם מורכבים. המונח Multithreading  פירושו שני  tasksאו יותר הפועלים "לכאורה" בו זמנית במסגרת אותה התוכנית. לעיתים קרובות מתייחסים אליו כאל "process קל" אולי כי האתגרים הכרוכים בהפעלתו הם, נו, בטח תפסת כבר את הרעיון

 

בשני המקרים השתמשתי בצירוף "לכאורה בו-זמנית" מכיוון שאם לתוכנית יש מעבד בודד,

ה processes לא ממש רצים בו-זמנית אלא משתמשים באותו מעבד על בסיס מסוים.

על כל פנים, במקרה של פלטפורמות בעלות כמה מעבדים יתכן שה processes אכן ירוצו בו-זמנית.

בשני המקרים, התנהגות מערכת ההפעלה יוצרת את הרושם של הרצה בו-זמנית.

חשוב מזה, multithreading  יכול לייצר תוכניות שיבצעו יותר עבודה באותו פרק זמן,

לאור העובדה שהמעבד משמש לכמה tasks.

Multiprocessing מוטמע במערכת ההפעלה.

כל שתי תוכניות או יותר הניתנות להרצה בפלטפורמה, ניתן להריץ על בסיס של multiprocessing מבלי שכותבי התוכנית יצטרכו לתכנן זאת מראש.

לעומת זאת, למרות ש- multithreading  בהחלט מחייב תמיכה מצד מערכת ההפעלה, בשורה התחתונה, הוא מוטמע בתוך התוכנה.

יותר מזה, תכנון מוקדם נדרש מצדו של כותב התוכנית, מכיוון שיש צורך שהיא תהיה בנויה במיוחד עבור הרצה בשיטת ה multithreading .

ולבסוף, שתי תוכניות multithreading או יותר ניתן להריץ בו-זמנית במערכת של multiprocessing. לכן, multithreading ו- multiprocessing  יכולים להתקיים בו-זמנית זה לצד זה.

 

למה אנו זקוקים ל- multithreading?

 

רוב הספרים מקדישים גם לסוגייה זו עמוד אחד או יותר.

שוב, בתמצית, תוכניות מסוגים רבים יכולים לעשות שימוש באופן יעיל יותר במשאבים מכניים זמינים על ידי הפרדת המשימות ל threads  שיפעלו בו-זמנית.

בחלק מה tasks יש פרקי זמן ארוכים שבהם לא מתבצעת שום פעולה תכליתית, בזמן שאחרות זקוקות באופן תמידי  לכל המשאבים הקיימים בסביבה. 

נניח לדוגמא שהיינו צריכים תוכנית שתוכל לקרוא 1000 מספרים מהמקלדת, תחשב את השורש של כל מספר ברמת דיוק של מאה מקומות אחרי הנקודה, ותעתיק את התוצאות לקובץ על התקליטור.

רוב הפעולות הקשורות בקלט של המקלדת צורכות מעט מאוד משאבים של המחשב.

בתוכנה סדרתית טהורה, כמו שרגילים לראות ב-   ,Pascal C , ו ++C , חלק גדול מהזמן המחשב יבזבז בהמתנה לכך שהאופרטור של המקלדת יקיש על מקש ה- enter ורק בחלק קטן מהזמן הוא למעשה יריץ נתונים מהבאפר של המקלדת.

באופן דומה, אם כי הרבה פחות חריף, גם תוכנית שצריכה להעתיק נתונים לקובץ תקליטור מבזבזת זמן, כשמערכת ההפעלה מאתרת את הקובץ, ממקמת את הראש-כותב  וכו' , לפני שבכלל מועברים נתונים כלשהם.
אם כך, חלק גדול ממשאבי המחשב מתבזבז על קלט/פלט  (input/output)של פעולות המקלדת ופעולות אחרות.

אני מעולם לא ניסיתי לכתוב תוכנית שתוכל לחשב את השורש של מספר ברמת דיוק של מאה מקומות אחרי הנקודה , בכל אופן, אני בטוח שהיא תצרוך את כל משאבי  המחשב שביכולתה לאגור, לפחות בפרצים קצרים (short bursts).

אם היינו כותבים את התוכנית שלנו כך ש:

- הקלט של המקלדת יטופל ע"י אחד מה threads

- פלט של התקליטור יטופל ע"י thread שני

- והחישוב יטופל ע"י thread שלישי

 

ל- thread  שיטפל בחישוב תהיה גישה לרוב משאבי המחשב בזמן ששני ה threads  האחרים יהיו 'חסומים' בהמתנה לתקשורת עם המקלדת ועם הבקר (controller) של התקליטור.

המונח 'חסום' פירושו שה- thread  מחכה שמשהו יקרה ולא משתמש בינתיים במשאבים של המחשב.

לתוכניות סדרתיות אין את היתרון הזה. בעוד שניתן לכתוב תוכניות שרצות במצומצם של לולאות בקרה, בוחנות מגוון מצבים ומכניסותtasks  לתורים, זו כנראה שיטה פחות יעילה מאשר threading.

מובן שבאמצעותthreading  כל מה שהשגנו הוא העברה של ה processes שחוזרים על עצמם מהאפליקציה למערכת ההפעלה, אבל נוכל לקוות שמתכנתי המערכת יוכלו לעשות עבודה טובה יותר משלנו במימוש process כמו זה.

בין אם הוא טוב יותר ובין אם לא, צריך לתכנת אותו רק פעם אחת - במהלך הפיתוח של מערכת ההפעלה, ולא לתכנת כל פעם מחדש בדרגות שונות של יעילות עבור כל תוכנת יישום חדשה.

           

שתי דרכים לבצע threading

 

יש שתי דרכים בג'אווה ליצור תוכניות בעלות threads:

- ליישם את הממשק הניתן להרצה (Runnable interface)

-  לירוש מהמחלקה Thread

 

אתחיל בכך שאציג לכם דוגמא פשוטה עבור כל אחת מהשיטות. יש כותבי תוכניות הגורסים כי השיטה הראשונה היא היותר מונחית-עצמים מבין השתיים.

בין אם זה נכון ובין אם לא, זו שיטה שבה ניתן כנראה להשתמש בכל המקרים, בעוד שבשיטה השנייה לא ניתן להשתמש במצבים מסוימים (ועל כך ארחיב מאוחר יותר).

 

יישום הממשק הניתן להרצה

 

כל מה שנידרש כדי 'לייצר' thread  בג'אווה הוא:

- ליצור מופעים לאובייקט מסוג Thread

- להפעיל את מתודת ה()run שלו

 

הקוד שישיג את המשימה המתבקשת מה thread (לבצע את העבודה) נמצא במתודה run() או במתודות  אחרות המופעלות על ידה.

אחת הדרכים ליצור מופעים לאובייקט מהמחלקה Thread  (שתהיה הנושא של אחד מהפרקים הבאים) היא:

· לרשת ממחלקת ה-Thread   לתוך מחלקה משלך.

אחר כך, דרוס את המתודה run()  של המחלקהThread  במחלקה החדשה שלך.

 

חשוב: כדי להריץ של thread  למעשה, אתה לא מפעיל את המתודהrun()  על האובייקט שלך, אלא את המתודה start().

 

עם זאת, לפעמים אי אפשר לירוש מהמחלקה Thread  

מכיוון שאתה חייב לרשת ממחלקה אחרת כל שהיא. זכור כי ג'אווה אינה תומכת בירושה מרובה. בתיכנות של  Applets , לדוגמא, אתה חייב לירוש מהמחלקה Applet.

במקרים בהם לא ניתן לירוש מהמחלקה Thread , אתה יכול לבנות את המחלקה שלך כך שהיא תירש מכל מחלקה רצויה אחרת , וגם כך שהיא תיישם את הממשק הניתן להרצה.

אם כך, הדרך השנייה (שהיא הנושא של פרק זה) לעשות מופעים  לאובייקט של המחלקה Thread היא:

-  ממש את הממשק הניתן להרצה במחלקה שלך (תוך ירושה ממחלקה אחרת אם יש צורך)

-  לאחר מכן, צור מופעים לאובייקט מהמחלקה שלך

כמו מקודם, אתה צריך לדרוס את המתודהrun()  של המחלקה Thread  במחלקה החדשה שלך. גישה זו מומחשת בתוכנית ההדגמה

 

כפי שתוכל לראות, תוכנית זו מגדירה מחלקה חדשה המכונה MyThread היורשת מהמחלקה המכונה DoNothing  וממשת את הממשק הניתן להרצה.

אנו דורסים את המתוד run() המחלקה הנקראת MyThread כדי להציג מידע על ה thread  שמומש על ידי אובייקט ממחלקת MyThread.

הסיבה היחידה לירוש ממחלקת DoNothing בדוגמא פשוטה זו היא השימוש בנתיב הירושה האפשרי היחיד למחלקה של ג'אווה.

המטרה היא לדמות מצב שבו אתה חייב לירוש ממחלקה אחרת כל שהיא.

ירושה ממחלקה אחרת היא אינה חובה, וגישה זו פועלת באותה מידה של יעילות גם כשאתה לא יורש מאף מחלקה אחרת. 

הקוד בmain()  יוצר מופעים  לשני אובייקטים מסוגThread  תוך שימוש באחת מהפונקציות הבונות הזמינות משלThread   כפי שנראה להלן:

 

Thread myThreadA = new Thread(new MyThread(),"threadA");

 

 

 

 

הפונקציה הבונה המסוימת הזו דורשת שני פרמטרים: הראשון הוא אובייקט ממחלקה כלשהי הממשת את הממשק הניתן להרצה. הפרמטר השני, הוא מחרוזת הנותנת שם ספציפי ל- thread. (שים לב שהשם הניתן ל thread אינו תלוי בשם של משתנה-ההפניה המפנה לאובייקט ה thread). קיימות מגוון מתודות לתמרון מצב ה thread  על ידי התייחסות שמית אליו.

קוד נוסף ב main()  מתחיל את הרצת שני ה  threads על ידי הפעלת המתודהstart()  על כל אחד מהאובייקטים של ה thread.

הthreads  נעצרים והתוכנית מסתיימת כש ל- threads  אין עוד מה לעשות.
(זה לא מה שקרה ב JDK 1.0.2 ).

דבר נוסף שצריך לשים לב אליו, הוא ש main הוא thread בפני עצמו, המופעל על ידי

המתרגם (interpreter) .

קוד במתודה run() גורם לכך שיוצגו השמות של כל אחד מה-threads  (יחד עם מידע נוסף)

לשני ה threads שיצרו להם מופעים ושהופעלו בתוכנית.

אם הכנסת קוד ב main()  גורמת להצגת מידע דומה לגבי ה-  threadשל main.

המתודה sleep() מופעלת על ה  thread של main לביצוע השהייה של שניה אחת.

במתודה sleep() ובמתודות נוספות שלThread , נדון במהלך הפרקים הבאים.

תוכנית זו מדגימה אחת מתוך שתי גישות אלטרנטיביות ליצירת מופעים ולהרצת threads  בתוכנית ג'אווה.

זו הגישה שחובה לאמץ בכל פעם שהמחלקה בה משתמשים כדי ליצירת מופעים לאובייקט של

הthread  צריכה לירוש ממחלקה אחרת כל שהיא. כפי שציינו קודם, גישה זו נכונה לא פחות גם למקרים שבהם לא נחוץ לירוש מחלקה אחרת.

זו הסקירה הכללית על שתי הגישות בהן דנו.

הגישה הבאה יכולה להיות יעילה כשהמחלקה בה משתמשים ליצירת מופעים לאובייקט ה  thread אינה נדרשת לירוש ממחלקה אחרת כל שהיא, כך שהיא יכולה לירוש מהמחלקה Thread.

 

ירושה מהמחלקה Thread

 

הבה נביט באותה תוכנית פשוטה  שיורשת ממהמחלקה Thread   ולא מיישמת את הממשק הניתן להרצה (Runnable interface).

 

בשיטה זו, המחלקה החדשה המכונה MyThread יורשת מהמחלקה  Thread ולא מיישמת את הממשק הניתן להרצה באופן ישיר.

(המחלקהThread  מיישמת את  Runnable כך ש- MyClass למעשה מממשת את  Runnable באופן עקיף).

בשאר המובנים, תוכנית זו פועלת כעקרון באופן דומה לתוכנית הקודמת.

 

גישה אחרת לירושה מהמחלקה Thread 

 

התוכנית הבאה גם היא יורשת מהמחלקהThread  אלא שהיא במידה מסוימת פשוטה יותר מהקודמת. בדוק אם אתה מסוגל להצביע על השוני.

רמז: בחן בקפידה את שמות

הthreads  הבודדים ושים לב כי יש להם שמות של ברירת מחדל. תוכנית זו משתמשת בגרסה אחרת של הפונקציות הבונות של Thread  מצריכה שום ארגומנטים. לכן, הפקודות ליצירת המופעים לאובייקטים של Thread  הן קצת פחות מורכבות. מלבד זאת, ביסודה היא די דומה לתוכנית הקודמת. זה התחביר שאתה צפוי לראות ברוב המקרים בספרים ובמאמרים שעוסקים ב multithreading.

תוכנית לדוגמה

 

עצירת threads

 

כפי שכבר הזכרנו, ה  threads נעצרים והתוכנית מסתיימת כשלכל הthreads  אין יותר מה לעשות. זה לא היה המצב ב JDK 1.0.2,

ולעיתים היה צורך להפעיל את מתודות הstop()    כדי לגרום לthread לעצור.

 

Daemon Threads

 

לפי הספר Java Primer Plus, אם אתה מגדירthread   כ- daemon thread  על ידי שימוש במתודה setDaemon() אתה קובע באופן מפורש שהthread  שייך למערכת ולא

ל- process שייצר אותו. Daemon threads  יעילים למקרה שאתה מעוניין שה thread  ירוץ ברקע לפרקי זמן ממושכים. לפי Deital & Deital  בספרם:

"Java, How to Program" ההגדרה של daemon thread
 07-12-03 / 20:07  עודכן ,  04-10-03 / 15:22  נוצר ע"י רונית רייכמן  בתאריך 

 Treads of control - הקודםהבא - תוכנית rum 
תגובות הקוראים    תגובות  -  0
דרכונט
מהי מערכת הדרכונט?
אינך מחובר, להתחברות:
דוא"ל
ססמא
נושאי לימוד
חיפוש  |  לא פועל
משלנו  |  לא פועל
גולשים מקוונים: 5