views:

103

answers:

3

So I have a method that gets a Dictionary of List<myObj>, then cycles through the keys of the dictionary and passes each List<myObj> to a separate thread.

Here is some Code / Psuedo-Code:

public static void ProcessEntries() {

    Dictionary<string, List<myObj>> myDictionary = GetDictionary();

    foreach(string key in myDictionary.keys)
    {

        List<myObj> myList = myDictionary[key];

        Thread myThread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate() {

            ProcessList(myList);

        }    
    }
}

public static void ProcessList(List<myObj> myList) {

    // Process entries
    // read-only operations on myList

}

The problem is that during execution of ProcessList the myList parameter simply changes.

I have looped through the list before kicking of the thread, and then immediately inside the thread, and I've found the results to be different.

I have since solved the problem (I think!) by making the Dictionary variable global. Using the [ThreadStatic] property is next on the list of possible fixes.

What I really want to know is why does the myList object changes inside ProcessList() presumably when the myList object is re-assigned in ProcessEntries() ? Are these not two different Lists ? If all parameter passing is by value by default, why does the ProcessList() function not have a local copy of myList ? (does it?)

Is there a way to specify that you want to pass a parameter to a thread and not have it be altered by the parent thread or other threads during execution? (This would be similar to the [ThreadSafe] attribute for global variables)

A: 

You are passing a reference by value in that case, so if you modify it somewhere it will be different everywhwere.

BioBuckyBall
+2  A: 

I suspect your pseudo-code isn't actually an accurate reflection of your real code. I suspect your real code looks like this:

foreach(var pair in myDictionary)
{
    Thread myThread = new Thread(delegate() {
        ProcessList(pair.Value);
    });
    myThread.Start();
}

If that's the case, the problem is that the pair variable is being captured - so by the time your thread starts, it may be referring to a different key/value pair.

The way to fix it is to make the code precisely more like your pseudo-code:

foreach(var pair in myDictionary)
{
    // You'll get a new list variable on each iteration
    var list = pair.Value;
    Thread myThread = new Thread(delegate() {
        ProcessList(list);
    });
    myThread.Start();
}

See Eric Lippert's blog post on this for more information.

If this isn't what's going wrong, please give a real example rather than pseudo-code. A short but complete example demonstrating the problem would be ideal.

Jon Skeet
A: 

Also be sure that other threads don't affect the thread you're trying to use. be sure to use locks and monitors... Had some issues with that just few weeks ago..

nomail