<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>&#8235;try, catch, write - Ron Klein &#187; כללי&#8236;</title>	<atom:link href="http://heblog.ronklein.co.il/category/%d7%9b%d7%9c%d7%9c%d7%99/feed/" rel="self" type="application/rss+xml" />
	<link>http://heblog.ronklein.co.il</link>
	<description>&#8235;הבלוג של רון קליין: תכנות, OOP, טכנולוגיה, Design Patterns, log4net ועוד עניינים&#8236;</description>	<lastBuildDate>Tue, 03 Aug 2010 11:18:49 +0000</lastBuildDate>
	<language>he</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>&#8235;תור מקבילי &#8211; Concurrent Queue&#8236;</title>		<link>http://heblog.ronklein.co.il/2009/06/concurrent-queue/</link>
		<comments>http://heblog.ronklein.co.il/2009/06/concurrent-queue/#comments</comments>
		<pubDate>Sun, 28 Jun 2009 21:00:05 +0000</pubDate>
		<dc:creator>&#8235;ronklein&#8236;</dc:creator>				<category><![CDATA[כללי]]></category>
		<category><![CDATA[Multi-Threading]]></category>

		<guid isPermaLink="false">http://www.ronklein.co.il/qwe/?p=4</guid>
		<description><![CDATA[&#8235;תור מקבילי הוא תור מותאם לעבודה שהיא multi-threaded. למצוא משהו כזה בדוט נט זה לא כל כך פשוט...&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p>רוב המתכנתים מכירים מבני נתונים בסיסיים, כמו מערך, רשימה מקושרת, וגם תור ומחסנית. הבעיה מתחילה כשרציתי למקבל תור. כלומר, ליצור מחלקה עם התכונות של תור, לצד העובדה שיכולים לגשת אליה במספר ת'רדים במקביל (אני לא מת על התרגום ל&quot;חוטים&quot;, ומי שרוצה לתת תרגום אחר &#8211; תעירו חופשי).</p>
<p>נשמע פשוט, נכון? כולה לעטוף תור קיים ומוכח בנעילה קטנה, ויש לך תור מקבילי.</p>
<p>אז זהו, שלא.</p>
<p>כדי להמחיש את העניין, הנה המימוש הנאיבי (&quot;כולה לעטוף תור קיים&quot; וכו' וגו') של תור מקבילי:</p>
<p dir="ltr">
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">class</span> NaiveConcurrentQueue<span class="sy0">&lt;</span>T<span class="sy0">&gt;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">private</span> Queue<span class="sy0">&lt;</span>T<span class="sy0">&gt;</span> queue <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Queue<span class="sy0">&lt;</span>T<span class="sy0">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">public</span> <span class="kw1">void</span> Enqueue<span class="br0">&#40;</span>T item<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>queue<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; queue.<span class="me1">Enqueue</span><span class="br0">&#40;</span>item<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw1">public</span> T Dequeue<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">lock</span> <span class="br0">&#40;</span>queue<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> queue.<span class="me1">Dequeue</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>בכוונה אין כאן את כל המתודות והממברים של תור, רק את השתיים האלו, כי זאת רק דוגמה. אז כאמור, רק נעילה סביב כל מתודה/ממבר של התור הפנימי, ויש לנו מקבול. אבל חבר'ה חבר'ה, רגע רגע, מה זה בדיוק נעילה? (לא, מוטל, <a title="תפילת נעילה - ויקי" href="http://he.wikipedia.org/wiki/%D7%A0%D7%A2%D7%99%D7%9C%D7%94" target="_blank">התפילה המסיימת ביום כיפור</a> זה לא מה שהתכוונתי)</p>
<p>נעילה בקוד, או lock, זה סוג של מאקרו ברמת הקומפיילר, שמאפשר לנו, המתכנתים העצלנים, לכתוב קוד שאמור לרוץ ע&quot;י ת'רד אחד בכל פעם. למה מאקרו? כי פיסת הקוד הבאה:</p>
<p dir="ltr">
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">lock</span> <span class="br0">&#40;</span>x<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; DoSomething<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>למעשה הופכת להיות הקוד הבא:</p>
<p dir="ltr">
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw5">System</span>.<span class="kw4">Object</span> obj <span class="sy0">=</span> <span class="br0">&#40;</span><span class="kw5">System</span>.<span class="kw4">Object</span><span class="br0">&#41;</span>x<span class="sy0">;</span><br />
<span class="kw5">System.<span class="me1">Threading</span></span>.<span class="me1">Monitor</span>.<span class="me1">Enter</span><span class="br0">&#40;</span>obj<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="kw1">try</span><br />
<span class="br0">&#123;</span><br />
&nbsp; DoSomething<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<span class="kw1">finally</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw5">System.<span class="me1">Threading</span></span>.<span class="me1">Monitor</span>.<span class="me1">Exit</span><span class="br0">&#40;</span>obj<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p>נו, אז? סה&quot;כ עטיפה נוחה ל Monitor.Enter ול Monitor.Exit.</p>
<p>אז בואו נקרא עוד קצת על Monitor.Enter. מתוך MSDN, כל עוד מדובר במערכת Win32, הוא <a title="מיפוי דוט נט ל Win32" href="http://msdn.microsoft.com/en-us/library/aa302340.aspx#win32map_synchronizationfunctions" target="_blank">ממופה</a> לפקודה EnterCriticalSection. יופי, בואו נקרא <a title="תיאור מלא של EnterCriticalSection" href="http://msdn.microsoft.com/en-us/library/ms682608(VS.85).aspx" target="_blank">עוד קצת על EnterCriticalSection</a>, שוב מתוך MSDN. נתמקד במשפט אחד:</p>
<p dir="ltr">There is no guarantee about the order in which waiting threads will acquire ownership of the critical section</p>
<p>הממממם. מעניין. אז בואו נסתכל רגע על המצב הבא:</p>
<p>בזמן T0, הת'רד הנוכחי נועל משאב X, ומבצע משימה שלוקחת 5 שניות.</p>
<p>אחרי 2 שניות (נקרא לזה זמן T2), ת'רד נוסף, שנקרא לו מנדל, גם רוצה לנעול את משאב X, ולבצע משימה קטנטונת, שלוקחת 0.01 שניה. מכיוון ש X כבר נעול, מנדל מחכה.</p>
<p>שניה אחרי T2 (נקרא לזה זמן T3), ת'רד נוסף, שנקרא לו צביקי, גם רוצה לנעול את משאב X, ולבצע משימה קטנטונת, שלוקחת 0.01 שניה. שוב, מכיוון ש X כבר נעול, גם צביקי מחכה.</p>
<p>שניה אחרי T3 (נקרא לזה זמן T4), ת'רד נוסף, שנקרא לו פושקש, גם רוצה לנעול את משאב X, ולבצע משימה קטנטונת, שלוקחת 0.01 שניה. ושוב, מכיוון ש X עדיין נעול, גם פושקש מחכה.</p>
<p>עכשיו, סוף סוף עברו 5 שניות מאז T0, ומשאב X משתחרר. יש כרגע שלושה ת'רדים שממתינים ל X: מנדל, צביקי ופושקש. לפי התיעוד למעלה, <strong>סדר הביצוע של הת'רדים הוא לא בהכרח סדר ההגעה שלהם</strong>. כלומר, זה יכול להיות מנדל-צביקי-פושקש, וזה יכול גם להיות מנדל-פושקש-צביקי, וכל קומבינציה אחרת. בדקתי את העניין, וכתבתי תוכנית קטנה שממחישה את זה. ברוב ההרצות הסדר נשמר. בחלק מההרצות הסדר השתנה. זה מספיק כדי להמחיש שהסדר לא בהכרח נשמר.</p>
<p>בעיה. כי בדיוק רציתי תור, כלומר מבנה נתונים שישמור גם על סדר ההגעה של הת'רדים. מעין message queue.</p>
<p>&quot;ב retlang יש את זה מובנה&quot;, אמר לי <a title="הבלוג של רשף" href="http://reshefmann.com/blog/" target="_blank">רשף</a>. בדקתי. <a title="retlang - אתר הבית" href="http://code.google.com/p/retlang/" target="_blank">retlang</a> זו ספריה בדוט נט שהופכת את התיכנות האסינכרוני להרבה יותר פשוט, מבוסס הודעות, בלי הצורך המעיק בנעילות. כלומר, הספריה הזו עושה בשביל המתכנת את כל הנעילות שהוא צריך. המתכנת, מאידך, צריך ללמוד איך משתמשים בזה. אחרי כמה דוגמאות ומספר נסיונות &#8211; זה ממש בסדר. ובאמת יש שם channel שנקרא QueueChannel. הסתכלתי בקוד עצמו. יש שם שימוש ב lock. כלומר, גם שם יש את הבעיה הזו.</p>
<p>כמה מיילים לעולם, ומישהו הציע להסתכל על <a title="PLinq - wiki" href="http://en.wikipedia.org/wiki/Parallel_FX_Library" target="_blank">PLinq</a>, שיש שם מימוש כזה. אבל אני לא יכול להסתכן בספריה כמו Plinq היא עדיין CTP, ועוד לא יצאה לאור באופן רשמי. פעם נכוויתי מספריה לא בשלה (Atlas, למי שזוכר), ומאז כבר למדתי את הלקח.</p>
<p>גוגל! גוגל יעזור לי. רק צריך לבקש, ובעיקר לדעת <strong>מה לבקש</strong>. אז גיגלתי lock free queue, וחיפשתי גם ב stackoverflow, ומצאתי עולם ומלואו. עולם שלם של אלגוריתמים ומבני נתונים שמיועדים לסביבה שהיא Multi-Threaded ושאין בהם נעילות רגילות, אלא לכל היותר Interlock. אז גיגלתי וריאציות של lock free queue, והנה כמה דברים שמצאתי:</p>
<p>קודם כל, כבר עשו את זה. בכנס שנקרא בקיצור <a title="דף הבית של PODC96" href="http://www.podc.org/podc96/" target="_blank">PODC96</a> ובאריכות Fifteenth ACM Symposium on Principles of Distributed Computing, מתואר מבנה נתונים שמספק את דרישת FIFO, ב<a title="כמה מילים ולינק להורדה" href="http://www.cs.rochester.edu/u/michael/PODC96.html" target="_blank">מאמר</a> בעל השם הקצרצר: Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms. כותבי המאמר, מייקל וסקוט, או בקיצור MS, מתארים איך אפשר להשתמש בטכניקות קלות יותר מאשר נעילות (כמו <a title="Compare and Swap - Wiki" href="http://en.wikipedia.org/wiki/Compare-and-swap">CAS</a>, שבדוט נט זה Interlocked.CompareExchange) כדי לקבל את התוצאה הרצויה. התור (או הרעיון המרכזי שלו) נקרא בהרבה מקומות אחרים MS-Queue. לשם שינוי ה MS מציין את שמות המשפחה <a href="http://www.cs.rochester.edu/u/michael/">מייקל</a> ו<a href="http://www.cs.rochester.edu/u/scott/">סקוט</a>, ולא חברת תוכנה ענקית.</p>
<p>יש כבר מימוש לזה בשפות רבות, וגם בדוט נט (<span style="direction: ltr;">C#</span> ליתר דיוק). הבחורצ'יק הזה נותן לנו <a title="מימוש מלא בדוט נט" href="http://www.boyet.com/Articles/LockfreeQueue.html" target="_blank">מימוש מלא</a>.</p>
<p>שני ישראלים, אחד בשם ניר שביט ואחת בשם Edya Ladan Mozes (ואיך מתרגמים את זה לעברית?) מציגים במאמר בשם An Optimistic Approach to Lock-Free FIFO Queues שאפשר לייעל את האלגוריתם המקורי של MS-Queue ולהגיע ל throughput גבוה יותר (בין השאר). מה שנקרא, אנחנו על המפה. אה כן, הנה הלינק ל<a href="http://www.cs.tau.ac.il/~shanir/nir-pubs-web/Nir_Publications.htm" target="_blank">רשימת המאמרים המלאה</a> באתר של ניר שביט.</p>
<p>מה שנקרא, החכמנו.</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 1438px; width: 1px; height: 1px;">
<div id="nameplate">
<h1 id="name"><span class="fn n"><span class="given-name">Edya</span> <span class="family-name">Ladan Mozes</span></span></h1>
</div>
</div>
</div>]]></content:encoded>			<wfw:commentRss>http://heblog.ronklein.co.il/2009/06/concurrent-queue/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
