Основной вопрос блокировки в C # - PullRequest
0 голосов
/ 11 ноября 2010

Занятия:

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, но приложение выдает исключение вне диапазона.

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

Спасибо!

Ответы [ 3 ]

5 голосов
/ 11 ноября 2010

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

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

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

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

1 голос
/ 11 ноября 2010

На объект 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.

1 голос
/ 11 ноября 2010

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

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

...