C# C语言中的基本锁定问题#
课程包括:C# C语言中的基本锁定问题#,c#,.net,multithreading,locking,thread-safety,C#,.net,Multithreading,Locking,Thread Safety,课程包括: public class SomeCollection { public void IteratorReset() { index = -1; } public bool IteratorNext() { index++; return index < Count; } public int Count { get { return
public class SomeCollection
{
public void IteratorReset()
{
index = -1;
}
public bool IteratorNext()
{
index++;
return index < Count;
}
public int Count
{
get
{
return floatCollection.Count;
}
}
public float CurrentValue
{
get
{
return floatCollection[index];
}
}
public int CurrentIndex
{
get
{
return intCollection[index];
}
}
}
类ClassA
、ClassB
和ClassC
包含以下循环,循环遍历CollectionObj:
for (threadUnsafeClass.CollectionObj.IteratorReset(); threadUnsafeClass.CollectionObj.IteratorNext(); )
{
int currentIntIndex = threadUnsafeClass.CollectionObj.CurrentIndex;
float currentfloatValue = threadUnsafeClass.CollectionObj.CurrentValue;
// ...
}
由于我只阅读这3个类中的CollectionObj,所以我使用多线程加速,但我不太确定如何加强线程安全性。在检索CollectionObj时,我在ThreadUnsaceClass中添加了一个锁,但是应用程序抛出了一个超出范围的异常
感谢您的帮助
谢谢大家! 您只读取了
CollectionObj
属性,但随后您对该值所引用的对象进行了变异。请看这一点:
for (threadUnsafeClass.CollectionObj.IteratorReset();
threadUnsafeClass.CollectionObj.IteratorNext(); )
IteratorReset
和iteratorext
都通过更改index
的值来变异SomeCollection
。基本上,使用当前代码无法安全地执行此操作。例如,多个线程都可以同时调用IteratorNext()
。第一个调用返回true
,但在该线程有机会读取值之前,其他线程会使索引无效
为什么要将集合本身用于迭代?通常,您需要实现
IEnumerable
,并在GetEnumerator
中返回一个新对象。通过这种方式,不同的线程可以在同一集合上分别获得一个表示“它们的”光标的不同对象。他们都可以遍历它,并且都可以看到所有的值。锁定CollectionObj属性不会有帮助。一个可能的问题是,所有3个线程都在调用IteratorReset()
,这将索引设置为-1。想象一下这样的场景:A启动for循环,在被中断之前到达循环中的第一行。现在B进入并调用IteratorReset()
,然后被中断,让A再次运行。线程A执行CurrentIndex
属性,由于B正在运行,该属性在内部使用index=-1。动臂,超出范围异常
还有其他方法可以产生不好的结果,但这可能是最容易看到的。是否打算让所有三个线程各自完成每个项目?或者您是否希望A、B和C划分工作(如消费者队列)?三个类A、B和C中的每个类都引用SomeCollection对象,每个类都将尝试增加内部索引,从而导致错误。也就是说,您应该能够通过以下方式从多个线程读取数组中的对象:
public static object[] sharedList = new object[]{1,2,3,4,5};
public void Worker()
{
int localSum=0;
for(int i=0; i<sharedList.length; i++){
localSum += (int)sharedList[i];
}
}
publicstaticobject[]sharedList=newobject[]{1,2,3,4,5};
公职人员()
{
int localSum=0;
对于(int i=0;我感谢您的回答。所有线程都将在集合中的相同项上迭代。如果是这种情况,最好按照Jon Skeet的建议执行。如果您需要每个线程分割工作,可以更改SomeCollection的实现以在GetNext()上返回元组,在关键部分锁定该方法的实现。谢谢Jon。在GetEnumerator()中返回新对象会有什么影响考虑到我的集合通常非常大,循环部分被调用数十万次,对性能有什么影响?@alhazen:如果集合非常大,这将减少创建额外对象的影响-因为您不会在每次迭代中创建一个对象,整个循环只创建一次。如果确实需要,可以使用值类型想要,就像List
一样-但在你知道有问题之前我绝对不会这么做。很抱歉再次打扰你。我修改了SomeCollection
,以便它按照你的建议实现IEnumerable
。当前GetEnumerator()返回floatListCollection.GetEnumerator()
但我找不到一篇文章描述每次调用时返回新枚举数对象的方法。在这种情况下我可以使用克隆吗?谢谢。
public static object[] sharedList = new object[]{1,2,3,4,5};
public void Worker()
{
int localSum=0;
for(int i=0; i<sharedList.length; i++){
localSum += (int)sharedList[i];
}
}