2010-08-05 88 views
2

所以我有一个方法,获取列表字典<myObj>,然后循环通过字典的键,并将每个列表<myObj>传递到一个单独的线程。线程执行期间C#线程参数发生变化 - 为什么?

下面是一些代码/伪代码:

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 

} 

的问题是,ProcessList中的执行期间myList中参数简单地改变。

我踢过线程之前已经遍历列表,然后马上在线程内部,我发现结果是不同的。

我已经解决了问题(我想!),通过使字典变量全局。使用[ThreadStatic]属性位于可能修复列表的旁边。

我真的很想知道为什么myList对象在ProcessList()内部发生变化,这大概是在ProcessEntries()中重新分配myList对象的时候?这些不是两个不同的列表吗?如果所有参数传递都是默认值,那么为什么ProcessList()函数没有myList的本地副本? (是吗?)

有没有办法指定你想传递一个参数给一个线程,而不是在执行过程中被父线程或其他线程改变了? (这将类似于全局变量的[ThreadSafe]属性)

+1

你确定你的代码/ psuedocode是正确和完整的吗?从我所看到的,虽然你对发生的事情的假设是错误的,但它看起来像代码应该工作得很好。 – 2010-08-05 16:57:19

回答

0

在这种情况下,您正在传递参考的值,所以如果您在某处修改它,它将会每隔一定时间会有所不同。

2

我怀疑你的伪代码实际上并不是你的真实代码的准确反映。我怀疑你的真正代码如下所示:

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

如果是这样的话,问题是,pair变量被抓获 - 所以你的线程开始的时候,它可能是指不同的密钥/值对。

解决它的方法是使代码更精确喜欢你的伪代码:

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(); 
} 

更多信息请参见Eric Lippert's blog post on this

如果这没有什么问题,请给出一个真实的例子而不是伪代码。 A short but complete example demonstrating the problem将是理想的。

1

还要确保其他线程不会影响您尝试使用的线程。一定要使用锁和显示器...几个星期前有一些问题..