2010-11-10 5 views
0

Классы:Базовый замок вопрос в C#

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]; 
     } 
    } 
} 

класса, который содержит ссылку на 'SomeCollection':

public class ThreadUnsafeClass 
{  
    public SomeCollection CollectionObj 
    { 
     get 
     { 
      return collectionObj; 
     }    
    }  
} 

Классы ClassA, ClassB и ClassC содержат следующий цикл, который перебирает CollectionObj:

for (threadUnsafeClass.CollectionObj.IteratorReset(); threadUnsafeClass.CollectionObj.IteratorNext();) 
{ 
    int currentIntIndex = threadUnsafeClass.CollectionObj.CurrentIndex; 
    float currentfloatValue = threadUnsafeClass.CollectionObj.CurrentValue; 

    // ...  
} 

Поскольку я всего лишь чтение CollectionObj в 3 классах, я использую многопоточность для ускорения, но я не совсем уверен, как обеспечить безопасность потоков. Я добавил блокировку в ThreadUnsafeClass при извлечении CollectionObj, но приложение выдает исключение вне диапазона.

Любая помощь приветствуется.

Спасибо!

ответ

5

Вы только читаете свойство CollectionObj, но тогда вы мутируете объект, на который ссылается значение. Смотрите этот бит:

for (threadUnsafeClass.CollectionObj.IteratorReset(); 
    threadUnsafeClass.CollectionObj.IteratorNext();) 

Оба IteratorReset и IteratorNext мутировать SomeCollection путем изменения значения index. В принципе, вы не можете сделать это безопасно с вашим текущим кодом. Например, несколько потоков могли бы позвонить, например, IteratorNext(). Первый вызов возвращает true, но перед тем, как этот поток получит возможность прочитать значения, другие потоки делают индекс недействительным.

Почему вы используете коллекцию для итерации? Как правило, вы реализуете IEnumerable<T> и возвращаете новый объект в GetEnumerator. Таким образом, разные потоки могли бы получить другой объект, представляющий «свой» курсор над той же коллекцией. Все они могли перебирать все, и все видят все ценности.

+0

Спасибо, Джон.Какое влияние оказывает возвращение нового объекта в GetEnumerator() на производительность, учитывая, что моя коллекция обычно очень большая, а секция цикла называется сотни тысяч раз? – alhazen

+1

@alhazen: если коллекция очень велика, это будет * уменьшать * влияние создания дополнительного объекта, потому что вы не создаете его на каждой итерации всего один раз для всего цикла. Вы * можете * использовать тип значения, если вы действительно хотите, например 'List ', но я определенно не буду этого делать, пока вы не узнаете, что у вас возникла проблема. –

+0

Извините, что снова вас обманули. Я модифицировал 'SomeCollection' так, чтобы он реализовал' IEnumerable ', как вы предложили. В настоящее время GetEnumerator() возвращает 'floatListCollection.GetEnumerator()', но я не смог найти сообщение, которое описывает способ возврата нового объекта перечислителя для каждого вызова. Могу ли я использовать клонирование в этом случае? Благодарю. – alhazen

1

Блокировка объекта CollectionObj не поможет. Одна из возможных проблем заключается в том, что все 3 потока вызывают IteratorReset(), который устанавливает индекс в -1. Представьте себе сценарий, в котором A запускает цикл for и переходит к первой строке цикла, прежде чем прерваться. Теперь B входит и вызывает IteratorReset(), а затем прерывается, чтобы снова запустить A. Thread A выполняет свойство CurrentIndex, которое внутренне использует index = -1 из-за запуска B. Бум, исключение за пределами исключения.

Есть и другие способы, которые могут привести к плохим результатам, но это, вероятно, проще всего увидеть. Предполагается ли, что все три потока проходят через каждый элемент самостоятельно? Или вы ожидаете, что A, B и C разделит работу (например, потребительскую очередь)?

+0

Благодарим вас за ответ. Все потоки будут перебирать те же элементы в коллекции. – alhazen

+0

Если это так, то что-то похожее на то, что предлагает Джон Скит, было бы лучше. Если бы вам понадобился каждый поток для разделения работы, вы могли бы изменить реализацию SomeCollection, чтобы вернуть кортеж в GetNext(), блокируя реализацию этого метода в критическом разделе. –

1

Объект SomeCollection ссылается на каждый из трех классов A, B и C, каждый из которых будет пытаться увеличивать внутренний индекс, вызывая ошибку (ошибки). Тем не менее, вы должны быть в состоянии прочитать объекты в массиве из нескольких потоков с чем-то вроде следующего:

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]; 
    } 
} 

Важным здесь является то, что каждый поток будет поддерживать свое собственное местоположение в пределах массива, в отличие от collectionObj ,

Смежные вопросы