multi-threading in C# - beginner's questions - Grasshopper2024-03-28T11:22:09Zhttps://www.grasshopper3d.com/forum/topics/multi-threading-in-c-beginner-s-questions?commentId=2985220%3AComment%3A1445546&feed=yes&xn_auth=noHi again Alessio,
Just had a…tag:www.grasshopper3d.com,2016-01-23:2985220:Comment:14455462016-01-23T03:03:14.420ZJames Ramsdenhttps://www.grasshopper3d.com/profile/JamesRamsden
<p>Hi again Alessio,</p>
<p>Just had a look at your most recent gh file. My thoughts aren't conclusive, but here's what I've found:</p>
<p>1) It seems you can update your dots (case 2) as you are updating a List, not a ConcurrentDictionary. Certainly, when I try to update a dictionary directly through an index (i.e. dictionary[key]) I get an error along the lines of the dictionary being read-only. Try testing a dictionary with a custom class, and a list with a Rhino class, and see what…</p>
<p>Hi again Alessio,</p>
<p>Just had a look at your most recent gh file. My thoughts aren't conclusive, but here's what I've found:</p>
<p>1) It seems you can update your dots (case 2) as you are updating a List, not a ConcurrentDictionary. Certainly, when I try to update a dictionary directly through an index (i.e. dictionary[key]) I get an error along the lines of the dictionary being read-only. Try testing a dictionary with a custom class, and a list with a Rhino class, and see what happens.</p>
<p>In any case, without further testing and some scientific rigour, I don't think it's right to decisively say that only custom classes can be updated in parallel loops.</p>
<p>2) I was able to get your case1 working with a slight modification. The second loop is:</p>
<p>System.Threading.Tasks.Parallel.ForEach(pts2, pt =><br/> {<br/> double height = rnd1.Next(1000) * 0.01;<br/> pts2.TryUpdate(pt.Key, new Point3d(Math.Cos(height), pt.Value.Y, height), pt.Value);<br/> }<br/> );</p>
<p>The 'strange results' you were getting looked like a classic case of parallel read/write conflicts. Not quite sure why you got this though. I found it strange that you looped through the list of indices, rather than the dictionary of points directly, as I have done. This might have something to do with why yours failed perhaps.</p>
<p></p>
<p>Anyway, keep your investigations coming! We're all learning through this together :)</p> James, thank you very much fo…tag:www.grasshopper3d.com,2016-01-10:2985220:Comment:14356082016-01-10T16:29:53.952ZAlessio Eriolihttps://www.grasshopper3d.com/profile/Ale2x72
<p>James, thank you very much for your answer and advice. Indeed I know multi-threading is difficult, this is why I started with baby steps. At this stage my concern was not so much about performance (but it will soon be), rather to gain an understanding why things that work are working and why things that do not work aren't working. Apparently, a Rhino class such as Point3d cannot be modified directly in a multi-threading loop but a custom class created within a C# component can (this is why…</p>
<p>James, thank you very much for your answer and advice. Indeed I know multi-threading is difficult, this is why I started with baby steps. At this stage my concern was not so much about performance (but it will soon be), rather to gain an understanding why things that work are working and why things that do not work aren't working. Apparently, a Rhino class such as Point3d cannot be modified directly in a multi-threading loop but a custom class created within a C# component can (this is why Vicente's algorithm works - Spring is a custom class he created).</p>
<p>To test this I created an updated version of the component that selectively uses a ConcurrentBag for Point3d (and, as expected, it does not work in updating the fields within members of the collection), a ConcurrentDictionary of Point3d (which gives... odd results, but I guess it has to do with some other complicated aspects of mutithreading that I still need to understand), and finally updates a List of a custom Dot class and then populates a Concurrent collection of Point3d. This last method succesfully updates fields in the custom class inside the loop.</p>
<p>I was just curious to know why Vicente's loop was working and now I found out that (so far) custom classes fields can be updated in a parallel loop, but still no idea why this is not possible with native classes (such as Point3d). I'll keep studying and make exercises and tutorials!</p>
<p></p>
<p>Again, thanks to both you and David for the advice!</p> "Are all of the new concurren…tag:www.grasshopper3d.com,2016-01-09:2985220:Comment:14348412016-01-09T01:02:43.964ZJames Ramsdenhttps://www.grasshopper3d.com/profile/JamesRamsden
<p>"Are all of the new concurrent collections lock-free?"</p>
<p>Answer: mostly, but not quite.</p>
<p><a href="http://blogs.msdn.com/b/pfxteam/archive/2010/01/26/9953725.aspx" target="_blank">http://blogs.msdn.com/b/pfxteam/archive/2010/01/26/9953725.aspx</a></p>
<p>"Are all of the new concurrent collections lock-free?"</p>
<p>Answer: mostly, but not quite.</p>
<p><a href="http://blogs.msdn.com/b/pfxteam/archive/2010/01/26/9953725.aspx" target="_blank">http://blogs.msdn.com/b/pfxteam/archive/2010/01/26/9953725.aspx</a></p> I attempted to 'fix' your fir…tag:www.grasshopper3d.com,2016-01-09:2985220:Comment:14350152016-01-09T00:52:59.941ZJames Ramsdenhttps://www.grasshopper3d.com/profile/JamesRamsden
<p>I attempted to 'fix' your first component, giving the code below, only to later realise that this is almost exactly what you'd made in your third component anyway! Here it is for argument's sake:</p>
<p></p>
<p>var pts = new List<Point3d>();<br></br> for (int i = 0; i < n; i++)<br></br> {<br></br> var temppt = new Point3d(i, 0, 0);<br></br> pts.Add(temppt);<br></br> }</p>
<p>var ptsbag = new System.Collections.Concurrent.ConcurrentBag<Point3d>();<br></br> var rnd = new…</p>
<p>I attempted to 'fix' your first component, giving the code below, only to later realise that this is almost exactly what you'd made in your third component anyway! Here it is for argument's sake:</p>
<p></p>
<p>var pts = new List<Point3d>();<br/> for (int i = 0; i < n; i++)<br/> {<br/> var temppt = new Point3d(i, 0, 0);<br/> pts.Add(temppt);<br/> }</p>
<p>var ptsbag = new System.Collections.Concurrent.ConcurrentBag<Point3d>();<br/> var rnd = new System.Random();</p>
<p>System.Threading.Tasks.Parallel.ForEach(pts, pt =><br/> {<br/> ptsbag.Add(new Point3d(pt.X + rnd.Next(0, 1000) * 0.01, pt.Y + 0.1, 0));<br/> }<br/> );</p>
<p>A = ptsbag;</p>
<p></p>
<p>The main difference between your example and mine is that you have used a ConcurrentBag, whereas I used a ConcurrentDictionary. When I wrote my own example, I'm pretty sure I tried the Bag, but didn't end up with a solution I liked.</p>
<p>The reason for this, as I understand, is that the Bag is unsorted, and therefore unindexed. In other words, you can't access an item in a Bag using the list[i] notation or similar. </p>
<p>The foreach loop allows us to go through the bag one item at a time, which is why we're able to read the data. But when we want to edit the pt and write it back to pts, the code no longer knows from where within pts pt came from, so it's unable to write. That's my guess anyway :)</p>
<p>The fix works because, when we save to pts, we are now adding a new item to the Bag, rather than attempting to overwrite an existing one.</p>
<p>One possible issue with the 'fix' is that I'm not sure whether accessing the single instance of rnd with multiple threads is threadsafe, and I would guess it isn't. Since creating a new instance of Random for every loop is inefficient, and crucially can cause non-randomness, I'm not sure what the best answer is here.</p>
<p>To echo David's word of warning, multithreading is hard, and we have to start asking many more questions of how our code is working. It can be very rewarding to get it right, but do start small, and test thoroughly. Read up the data types your are using on MSDN, especially checking for thread safety, and have a look at the various multithreading tutorials there too. </p>
<p></p> David, thank you for the tips…tag:www.grasshopper3d.com,2016-01-08:2985220:Comment:14346982016-01-08T22:23:52.894ZAlessio Eriolihttps://www.grasshopper3d.com/profile/Ale2x72
<p>David, thank you for the tips. I'll keep on searching and looking into tutorials, while keeping them in mind!</p>
<p>David, thank you for the tips. I'll keep on searching and looking into tutorials, while keeping them in mind!</p> I don't have time right now t…tag:www.grasshopper3d.com,2016-01-08:2985220:Comment:14347192016-01-08T19:39:27.784ZDavid Ruttenhttps://www.grasshopper3d.com/profile/DavidRutten
<p>I don't have time right now to delve into your code, but these are a few things to keep in mind when writing parallel algorithms:</p>
<ul>
<li>There are different ways to multi-thread code, and which one is right for you depends on the specific problem. If you wish to simultaneously perform distinct, long-running processes you will need a different approach (probably System.Threading.Task based) than if you need to perform loads of really short calculations.</li>
<li>Data which is accessed…</li>
</ul>
<p>I don't have time right now to delve into your code, but these are a few things to keep in mind when writing parallel algorithms:</p>
<ul>
<li>There are different ways to multi-thread code, and which one is right for you depends on the specific problem. If you wish to simultaneously perform distinct, long-running processes you will need a different approach (probably System.Threading.Task based) than if you need to perform loads of really short calculations.</li>
<li>Data which is accessed for reading from multiple threads may get duplicated by the runtime if it feels that is required. This will result in (potentially very significant) overhead.</li>
<li>Data which is accessed for writing from multiple threads is always a huge problem. Either the threads access the data simultaneously which can result in missing or corrupt data, or the threads must lock the collection during each write, which tanks performance. The collection types in the Concurrent namespace may well resort to locking and may thus be slow to use.</li>
<li>The single most useful design pattern for multi-threaded code is <em>immutability</em>. Design your classes in such a way that they <em>cannot</em> be changed once constructed. Consider the System.String type as a prime example of this pattern. Classes which are immutable are much safer to use because they cannot be corrupted after the fact.</li>
<li>Finally, writing multi-threaded code is hard. It's difficult to make it safe. It's difficult to debug. It's difficult to actually make it fast. Besides infinite recursion, there's no more efficient way to completely crash your program than writing MT code.</li>
</ul>