What's the proper syntax for C# multi thread?

Continuing the discussion from Parallel for loop:

My loose grasp of .NET isn’t enough to understand Microsoft’s documentation. Can someone help with the System.Threading.Tasks.Parrallel.For()?

I’m trying to write a component that goes through each BrepFace of a Brep, divide it into UV sub-surfaces, do something to these surfaces and output them. Pseudo-code looks like this:

protected void FaceOp(int index, int branchindex, ref DataTree output, BrepFaceList faces)
{

    // going thru each face of brep: faces[j]
    // divide by UV and do something to each patch
    // add that back into a DataTree
}

for (int i =0; i < AllBreps.Count; i++) // going into each brep
{
     Parrallel.For(0, AllBreps[i].Faces.Count - 1, j => FaceOp(j, i, ref tree, AllBreps[i].Faces));
}

The compiled component doesn’t throw an error but there is no output. I understand that the patches aren’t really added to my DataTree

I know that lambda function is probably wrong…

That someone is the Master:

http://www.albahari.com/threading/

3 Likes

I implemented a working version of my code by looking here.
Really it’s putting the entire method after the =>

Hi Will,
Its likely that you’ll get concurrency exceptions the way that code is written. These won’t be reported in Rhino as they’re happening on a separate thread, but you’ll be able to see them if you attach a debugger to Rhino.exe and enable CLR exceptions.

Essentially you shouldn’t modify a collection (or object, for that matter) from another thread unless you are using concurrent objects or operations, or are using locks to enforce it. To my knowledge datatrees are not thread safe.

A common way I deal with this is to write data to an array (writing to specific position in an array is a thread safe operation) and then format the data after the loop.

Some examples:

This code is not thread safe. It’s possible that two threads will attempt to write to myList at the same time.

List<int> myList = new List<int>();
Parallel.For(0, 100, i => {
	myList.Add(i*i);
});

This code is thread-safe (but inefficient). It uses a lock to ensure only one thread can write to the list at the same time. It’s acceptable when calculating the result takes a long time, but very inefficient if this is for small operations. You’re also not guaranteed to get the output in the same order as the threads may finish out of order.

List<int> myList = new List<int>();
object myListLock = new object();

Parallel.For(0, 100, i => {
    int result = i * i; // Let's pretend this is an expensive operation
    lock (myListLock)
    {
         myList.Add(result);
    }
});

This code is thread safe. It’s efficient, and it also ensures that the results are in the correct order as they write to the specific array index. We are sure that no other thread is writing to that index as we specify this in the loop.

int[] results = new int[100];
Parallel.For(0, 100, i => {
	results[i] = i*i;
});

Essentially, you want to ensure that any operation that affects something outside of the current thread is thread-safe. So it’s fine to make changes to a Brep, as long as no other thread is touching that Brep. Where you have to be careful is how you output the result, as you don’t want multiple threads completing and modifying a data tree at the same time.

7 Likes

what can happen to this thread-unsafe way of adding stuff to DataTree?
I’m calling DataTree.AddRange() with GH_Path as a second input parameter. The GH_Path is always unique. I don’t think anything is getting bumped. If two thread simultaneously access this DataTree does one become “discarded”?

Adding keys to a dictionary is not an atomic operation, so if two threads attempt to add two paths to the same tree at the sametime, it may leave the tree in an incorrect or even corrupt state. Anything from missing paths, to partial paths, to straight up crashes are possible.

I will try to use the locker object and see if multi threading that way saves me any time
Thanks!

Follow up question…
What if each thread knows exactly the key of which it is trying to change the value? In other words, can multiple threads simultaneously “read and write” to a dictionary, knowing they each modifies values under distinct keys that are already in the dictionary?

No, the code which updates the dictionary state in response to each new storage instruction is not thread-safe. You can probably read from a dictionary using as many threads as you like, but as soon as you start modifying it you need to lock it.

If you want to experiment with thread-safe collections then I highly recommend switching from things like HashSet<T>, List<T>, Dictionary<T> to their immutable counterparts in the System.Collections.Immutable namespace. I found using those classes with all their restrictions and constraints really helped me start to think about data storage in a whole new way.

If you don’t want to go the immutable route, then you can use System.Collections.Concurrent instead. These classes will tend to have locks build in whenever needed.

4 Likes