Threadpool threads and waiting for large number of threads to finish

This article is about waiting for a large number of ThreadPool threads to finish executing, and two exceptions that can occur in a winforms application when trying to wait for ThreadPool threads to finish. 

Long long time ago in the days of visual studio 2008  i was playing with ThreadPool trying to queue a large number of threads and waiting for them to finish. I recreated the issues I stumbled upon in the new and shiny visual studio 2010 express.I created a new windows forms application and tried out the following code:

 

private static void CreateLotsOfThreads()

{

    int numberOfThreads = 1000; // Large number of threads 

    // An array whichis global to all the threads where each thread reports

    // to a unique index when it finishes working

    ManualResetEvent[] ResetEvents = new ManualResetEvent[numberOfThreads];

    // Initialize ManualResetEvent objects with a non signaled state, causing threads

    // which execute WaitOne() on to block until the thread has finished working 

    for (int threadIndex = 0; threadIndex <numberOfThreads; threadIndex++)

        ResetEvents[threadIndex] = new ManualResetEvent(false); 

    for (int i = 0; i < numberOfThreads; i++)

    {

        // The index to set in the ResetEvents array when the current thread finishes.

        // The copy of i prevents the anonymous methods from sharing variables with the

        // outerscope (see remarks at the end of the article for more information)

        int guaranteedIndex = i; 

        // Queue ananonymous delegate for the example

        ThreadPool.QueueUserWorkItem(

            delegate

            {

                //Thread work

                Thread.Sleep(10);

                //Thread finished working

               ResetEvents[guaranteedIndex].Set();

            });

    }

    // Wait for allof the threads to finish

    WaitHandle.WaitAll(ResetEvents);

    // At this pointall the threads finished execution

}

       

When executing the above code, I received this error message:

System.NotSupportedException wasunhandled

 Message=The number of WaitHandles must be less than or equal to 64.

 

I changed the value of numberOfThreads to 64 and executed it again, what can possibly go wrong now? Well this time a different error message appeared:

System.NotSupportedException wasunhandled

 Message=WaitAll for multiple handles on a STA thread is not supported.

 

Windows Forms applications use a Single Threaded Apartment (STA) andWaitAll() supports only Multithreaded Apartment (MTA) so it cannot be usedhere, but does not matter, even if 64 threads were running my goal was to queue a much larger amount so this could not be of help anyway (And i don’t want to execute threads in batches of 64)

 

One solution to the problem is to wait for each thread individualy:

 

private void CreateAndWaitSolution1()

{

    int numberOfThreads = 1000; // Large number of threads

    ManualResetEvent[] ResetEvents = new ManualResetEvent[numberOfThreads];

 

    for (int threadIndex = 0; threadIndex <numberOfThreads; threadIndex++)

        ResetEvents[threadIndex] = new ManualResetEvent(false); 

    for (int i = 0; i < numberOfThreads; i++)

    {

        int guaranteedIndex = i; 

        ThreadPool.QueueUserWorkItem(

            delegate

            {

                Thread.Sleep(10);

               ResetEvents[guaranteedIndex].Set();

            });

   

    // Wait until allthreads set their ManualResetEvent state to signaled

    for (int i = 0; i < 1000; i++)

        ResetEvents[i].WaitOne();

    // At this pointall the threads finished execution

}

 

The above approach is simple, just iterate over the array which is global to all the threads and wait for each thread to finish. ManualResetEvent is threadsafe, and all of the instances are created before any thread is executed, so it should be pretty safe.

In the above code only the UI thread does the waiting, but it is possible to make a specific ThreadPool thread  wait for another ThreadPool thread to finishbefore it starts working, (assuming there is a dependency between the two threds),the code above allows this by using the ResetEvents array. Example:

 

ThreadPool.QueueUserWorkItem(

delegate

{

    // Make threadnumber 6 wait thread number 7 before it begins working

    if (guaranteedIndex== 5)

        ResetEvents[6].WaitOne();

    Thread.Sleep(10);                       

    ResetEvents[guaranteedIndex].Set();

});

 The above example shows how to make a specific ThreadPool thread waitfor another ThreadPool thread to finish before it begins working.

  

Back to the main subject, A different approach for waiting for threads to finish does not require the usage of ManualResetEvent, but it’s good only if threads do not depend on one another. It involves counting the number of threads which finished and periodicallychecking how many threads finished working from the main UI thread:

private void CreateAndWaitSolution2()

{

    int numberOfFinishedThreads = 0;

    int numberOfThreads = 1000; // Large number of threads

 

    for (int i = 0; i < numberOfThreads; i++)

    {

        intguaranteedIndex = i; 

        ThreadPool.QueueUserWorkItem(

            delegate

            {

                Thread.Sleep(10);

                //Atomic Increments and store

                Interlocked.Increment(ref numberOfFinishedThreads);

            });

    }

     // Wait until all threads incremented the numberOfFinishedThreads counter

    while(numberOfFinishedThreads < numberOfThreads)

        Thread.Sleep(100);

    // At this pointall the threads finished execution

}

 The above makes the ManualResetEvent array unnecessary and I assume that it saves some memory, the number of code lines  is not reduces dramatically though. 

 The Interlocked.Increment prevents a race condition which can cause two separate threads to increment the counter to the same value. The sleep duration can be tweaked to be more aggressive or less aggresive depending onthe amount of time you expect the threads to finish.

 

I was concerned about UI freeze in windows forms application so  I did a little experiment on the above code: I changed numberOfThreads to 10000 and added a textbox and a button to the

main application form, I then  executed the following in the Form load event:

 

private void Form1_Load(objectsender, EventArgs e)

{

    textBox1.Text = "working...";

    Thread thread = newThread(new ThreadStart(delegate

        {

            //Delayed start, just to make sure the form will show etc.

            Thread.Sleep(1000);

            CreateAndWaitSolution1();

            //CreateAndWaitSolution2();

            // Safelyupdate the UI thread from this thread

            this.Invoke(new MethodInvoker(delegate

            {

                textBox1.Text = "finished!";

            }));

        }));

    thread.IsBackground = true;

    thread.Start();

}

 

The form was responsive, I could drag it anywhere without lag and there was no problem pressing the button. CreateAndWaitSolution1 Finished much faster than CreateAndWaitSolution2 when both ran 10000 threads, the opposite of what i expected.  I guess Interlocked.Increment is responsible for that, it blocks threads when they try to  increment the counter value at the same time which makes the total execution time longer.

   

Some more information about Threapool: 

-  Threapool  is Good for executing many short-livedthreads, providing your application with a  pool of worker threads  that are managed by the system.

- ThreadPool threads are background threads and will not keep anapplication running after all foreground threads have exited.

- You cannot join on a threadpool thread

- You cannot assign priority to ThreadPool thread

- You cannot abort a specific ThreadPool thread

 

Remarks: 

 guaranteedIndex: Because the ThreadPoolexecutes threads “when a thread pool thread becomes available” The code insidethe anonymous delegate is executed only after the loop completes, giving eachthread the last value of i and not the value that i had when the delegate codewas put into the Threadpool queue.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: amirtal
Posted on: 5/11/2010 at 11:06 PM
Categories: .NET | Winforms
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

הכנסת null כערך ל-textbox

נושא קטן, ישן ומעצבן ששוב בזבז שעות עבודה מיותרות. בניגוד לכל הדפדפנים האחרים, כאשר מנסים להכניס לתיבת טקסט ערך null באמצעות javascript, הדפדפן IE "מגדיל ראש" שלא לצורך ובמקום לאפס את הערך למחרוזת ריקה, הוא מכניס את המחרוזת "null". מה שמפתיע יותר הוא שגם IE8, שכבר מתחיל להתקרב יותר ויותר להיות דפדפן שעובד לפי התקנים, גם הוא ממשיך עם ההתנהגות הזו. ככל הנראה הסיבה להתנהגות זו היא ש-MS רצו בזמנו להקל על התוכניתנים ולהבין מחרוזות הכתובות ללא גרשיים כמחרוזות, ופשוט שכחו להתייחס למקרה המיוחד של null. אפשר לראות בזה באג כיוון שבכל הדפדפנים האחרים זה עובד בצורה תקינה (לפחות בבדיקה מול: chrome, safari, firefox).
 
אגב, התופעה משתחזרת גם בדפדפני IE ישנים יותר, כגון IE6 ו- IE7 (אשר עדיין בשימוש בפלחים גדולים באוכלוסיה).
 
ניתן לשחזר בצורה מאוד פשוטה באמצעות דף html. פשוט להעתיק את שתי השורות הבאות ולהריץ:

<input type='text' id='txt' />
<input type='button' onclick="document.getElementById('txt').value=null;" value='insert null' />

 
התיקון פשוט מאוד.סה"כ יש להחליף את null במחרוזת ריקה:
 

<input type='text' id='txt' />
<input type='button' onclick="document.getElementById('txt').value='';" value='insert null' />

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: eladv
Posted on: 11/17/2009 at 11:09 PM
Tags: , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

המדריך לדו"חות: איך חברה מרוויחה ואין לה כסף?

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

 

מה מייצג דו"ח רווח והפסד ומדוע צריך דו"ח תזרים מזומנים, מה למשק בית ודו"ח כספי של חברה, ואיך חברה יכולה להרוויח בספרים, אך להישאר ללא מזומנים? פרק שני במדריך

 

מצ"ב קישור לכתבה ב-ynet.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: uris
Posted on: 10/21/2009 at 9:51 PM
Tags: , , , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Visual Studio 2008 Design View Freezing Solved

Recently we had an issue with VS2008 freezing (stuck, not responding... you name it...) when you try to switch to design-view of a web page (aspx).

Since we merely changed the markup lately, it's been quite annoying.

This issue was very time consuming as the web offered no solution for this behavior (the solutions offered weren't helpful - at least for our case).Cry

 

Having a tight deadline in our project, we really had to solve this issue quickly and decided to use brute-force by tearing the Master Page (which seemed to be causing this) apart for finding the problematic markup.

I repeatedly removed blocks of markup and tried to switch to design-view after each block was removed.

Most of the time switching to design-view caused the devenv.exe (VS2008) to stop responding and I had to kill the process (via the Task Manager).

After a while, I have found the markup that was preventing me from switching to design-view.

 

For what it's worth, the markup is presented here: (Very weird, can't see why this should be problematic).

<asp:Label ID="lblOrganisation" CssClass="changeOrganization" runat="server" Font-Names="Arial"

Font-Size="12pt" Height="23px" Text="Switch Organization:" meta:resourcekey="lblOrganisationResource1">

</asp:Label>

Shall you have another solution or an explanation why Height="23px" (the block that I removed) should cause this, please leave a comment.

 

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: uris
Posted on: 10/21/2009 at 9:12 PM
Tags: , , , , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

איך גורמים ל-Apple MacBook Air SuperDrive לחזור ולהופיע בויסטה

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

חיפשתי רבות באינטרנט עד שמצאתי פתרון הולם והאמת החיפוש היה שווה... במקום להתקין הכל (אבל הכל) מחדש (כפי שהציעו בהרבה פורומים) קיבלתי פיתרון אלגנטי שעוקף את הבעיה בזריזות ומאפשר לחזור ולהנות מהכונן אפילו בלי לעשות restart.

המקור הוא פה:

http://discussions.apple.com/thread.jspa?threadID=1741817&tstart=-1  (מי שכתב את הפתרון הוא sieken)

 

השלבים הם פשוטים:

1. להיכנס לניהול מערכת (לחיצה על המחשב שלי עם הלחצן הימני של העכבר).

2. בוחרים עם הלחצן הימני של העכבר את External SuperDrive ובוחרים ב-Update Driver (עדכון מנהל התקן).

3. באשף שנפתח בוחרים שלא לחפש באופן אוטומטי אלא להתקין באופן ידני מנהל התקן שנבחר (Install from specific location -> Don't search).

4. בוחרים ב-USB Mass Storage Device ולוחצים על "סיום".

5. לאחר שהאשף נסגר בוחרים שוב עדכון מנהל התקן (שימו לב שהכונן שינה את שמו ל-USB Mass Storage Device).

6. חוזרים על שלב 3.

7. חוזרים על שלב 4 רק שהפעם חוזרים למנהל ההתקן המקורי "Apple SuperDrive" ולוחצים על "סיום".

 

מקווה שעזרתי לנפש אחת או שתיים - לי זה חסך כמה שעות מיגעות מאוד.

 

בהצלחה,

אורי.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: uris
Posted on: 10/11/2009 at 4:11 PM
Tags: , , , , , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

איך קוראים דו"ח כספי? המדריך לשפת המספרים

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

 

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

 

מצ"ב קישור לכתבה ב-ynet.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: uris
Posted on: 10/10/2009 at 12:12 AM
Tags: , , , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

קוד מקור לטובת דיבאג

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

יש לפתוח את האפשרויות ב- Tools->Options->Debugging ולבטל את Just My Code ולשפעל את Enable source server support.

יש לקנפג את המקום ממנו ייטענו ה-debugging symbols ולאן יישמרו (Tools->Options->Debugging->Symbols):

הכתובת היא: http://referencesource.microsoft.com/symbols.

הערה: החלון עשוי להראות שונה בין גרסאות ה-Visual Studio השונות.

כאשר מגיעים לקטע שאותו מעוניינים לדבג (framework, לא קוד שלנו), ב-call stack בוחרים ב-Load Symbols.


  

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

כפי שניתן לראות, ה-Symbols נטענו עבור הקבצים המבוקשים וכעת ניתן לבצע Step Into.

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

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: eladv
Posted on: 4/11/2009 at 10:14 AM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

היכרות קצרה עם סקראם (Scrum) - חלק א'

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

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

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

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

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

אחת מהמתודולוגיות הללו היא Scrum.

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

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

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

 

בסרטון הבא תמצאו תיאור מוצלח של scrum ב-10 דקות

מעורבות הלקוח הינה חלק בלתי נפרד מפרויקט scrum טיפוסי.

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

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

המשך יבוא (חלק ב')....

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: uris
Posted on: 4/6/2009 at 11:53 PM
Tags: , , , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

סיפור לקוח - מערכת לאיבחון פסיכולוגי ממוחשב

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

חברת פסיפס יישומים פסיכולוגיים בע"מ הוקמה בשנת 1993 והיא מנוהלת ע"י ד"ר פרץ סוֹכר
ואבי בן יחזקאל (M.A.), פסיכולוגים בעלי ניסיון רב בתחום הפסיכולוגיה החברתית-תעסוקתית.

להלן תיאור הפרויקט מאת ד"ר פרץ סוֹכר.

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

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

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

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

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

אורי שמחוני, מנכ"ל משותף בפרופר דבלופמנט:

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

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

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

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: uris
Posted on: 12/25/2008 at 7:42 PM
Tags: , , , , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Lambda Expressions

קהל יעד

תוכניתני C# 2 העובדים ב-Generics וכן מכירים מספיק טוב שימוש ב-delegates ומעוניינים לבחון את האפשרות לעבור ל-C# 3, או כאלה שכבר עובדים ב-C# 3 ואוהבים לקרוא בנושא.

כללי

Lambda Expression היא יכולת מסוימת שאם ננסה להסבירה בצורה פשוטה, נגדיר זאת כך: היכולת שלנו לכתוב Anonymous Delegates/Methods בצורה פשוטה יותר.

Anonymous Methods?

נניח שאנו קולטים ב-Console Application מספרים מהמשתמש לתוך מערך של string. לאחר שקלטנו מערך של מחרוזות (strings), אנו נרצה להמיר אותם למספרים. דרך אחת, היא באמצעות foreach:

// input in string format
string[] input = new string[] { "1", "2", "3", "4", "5", "6" };           

// convert to int typed list
List<int> list = new List<int>();
foreach (string s in input)
   list.Add(int.Parse(s));           

// to array...
int[] result = list.ToArray();

 

דרך נוספת, היא להשתמש ב- Array.ConvertAll< >:

// input in string format
string[] input = new string[] { "1", "2", "3", "4", "5", "6" };    

// to array...
int[] result = Array.ConvertAll<string, int>(input, MyConvert);
private static int MyConvert(string s)
{
   return int.Parse(s);
}

Array.ConvertAll< > סה"כ מבצע בדיוק את מה שעשינו קודם לכן: foreach וקריאה ל-MyConvert, היא הפונקציה שלנו, המתרגמת מחרוזת בודדת ל-int. קצת מסורבל. כתבנו פונקציה "שלמה" לטובת מטרה פשוטה: לבצע int.Parse.

באמצעות Anonymous Method אפשר לפשט את הדברים:

// input in string format

string[] input = new string[] { "1", "2", "3", "4", "5", "6" };

   

// to array...

int[] result = Array.ConvertAll<string, int>(input, delegate(string s)

{

   return int.Parse(s);

});

 

למעשה, שמנו את גוף הפונקציה MyConvert ישירות בתור ארגומנט ל-Array.ConvertAll< >. אם נסדר קצת את הקוד ונצמצם שורות מיותרות, נגיע לתוצאה הבאה:

int[] result = Array.ConvertAll<string, int>(input, delegate(string s) { return int.Parse(s); });

 

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

Lambda Expressions

באמצעות C# 3, ניתן לכתוב Lambda Expressions אשר יפשטו עוד יותר את הדברים. לפני שרצים לקוד, כדאי להקדים ולומר, שהקומפיילר הולך ונהיה יותר ויותר "חכם". למעשה, הוא כ"כ חכם, שהוא פשוט מסיק הרבה דברים לבד. בדוגמא הקודמת, הקומפיילר הסיק בעצמו שערך ההחזר הוא מסוג int (לפי ה-Converter delegate וה-generics שצוינו). הקומפיילר נהיה עוד יותר "חכם" בגרסה 3, ולכן מתאפשרת הפשטה של הקוד. אנו נדגים זאת בשלבים:

  1. השלב הראשון דווקא לא קשור ל-lambda expression. הקומפיילר לבדו מבין שמדובר ב-string input ו-int output, שכן הוא מבחין בסוגי הפרמטרים הנדרשים. לכן, ניתן להוריד את ההגדרות המפורשות:

int[] result = Array.ConvertAll<string, int>(input, delegate(string s) { return int.Parse(s); });

 

  1. ברור לקומפיילר שמדובר ב-delegate. הוא מסיק זאת לפי הארגומנט המופיע בחתימה של Array.ConvertAll< >. לכן ניתן להשמיט את המילה המיותרת:

int[] result = Array.ConvertAll(input, delegate(string s) { return int.Parse(s); });

 

  1. בצורה דומה, אפשר להשמיט את ה-string בארגומנט של ה-delegate, כיוון שהקומפיילר מסיק זאת לפי העובדה ש-input הוא מערך של מחרוזות:

int[] result = Array.ConvertAll(input, (string s) { return int.Parse(s); });

 

  1. העובדה שמדובר ב-delegate שמחזיר ערך, ברורה לקומפיילר מהחתימה של Converter delegate (בדיוק כפי שהוא הסיק שמדובר בערך החזר מסוג int). לכן גם המילה return מיותרת:

int[] result = Array.ConvertAll(input, (s) { return int.Parse(s); });

 

  1. מה שנשאר הוא ארגומנט s וגוף הפונקציה:

int[] result = Array.ConvertAll(input, (s) { int.Parse(s); });

  1. במטרה להשמיט את הסוגריים המיותרים הן מהארגומנט והן מגוף הפונקציה, C# 3 מספק לנו אופרטור חדש. התוצאה, קרויה lambda expression:

int[] result = Array.ConvertAll(input, s => int.Parse(s));

 

 

  1. בהשוואה אל מול ה-anonymous delegate שכתבנו קודם (ללא ה-generics):

int[] result = Array.ConvertAll(input, delegate(string s) { return int.Parse(s); });

אל מול:

int[] result = Array.ConvertAll(input, s => int.Parse(s));

 

כאשר מבינים lambda expression כהפשטה של Anonymous Method, כאשר צד שמאל של האופרטור <= הוא הארגומנט, וצד ימין הוא גוף הפונקציה, הדברים ברורים למדי. מה שנשאר הוא לתרגל ולהפנים.

סיכום

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

מה צריך בשביל זה?

.NET Framework 3.5 ו-Visual Studio 2008 או מאוחר יותר.

קישורים נוספים

Anonymous Methods

Lambda Expressions

C# 3.0 and LINQ - Expression Trees

Currently rated 4.6 by 7 people

  • Currently 4.571429/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: eladv
Posted on: 12/22/2008 at 12:36 AM
Tags: , , , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (3) | Post RSSRSS comment feed