добавить строки в таблицу данных с помощью parallel.for

У меня есть этот саб:

Private Sub error_out(ByVal line As Integer, ByVal err_col As Integer, ByVal err_msg As String)

            Dim ln = t_erori.Rows.Add
            ln.Item(0) = line
            ln.Item(err_col) = err_msg
            ln.Item(3) = err_col
    End Sub

Это вызывается несколькими функциями, работающими в цикле parallel.for.

Проблема в том, что иногда (совершенно случайно) я получаю сообщение об ошибке:

Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index

на Dim ln = t_erori.Rows.Add строке.

Я подозреваю, что это потому, что он пытается дважды добавить одну и ту же строку. Как я могу заставить это работать? Или какой еще метод я мог бы использовать для этого?

Мне нужна эта таблица данных, потому что мое приложение записывает туда некоторые результаты, но подойдет любой другой метод хранения результатов, который работает с parallel.for.


person Iulian    schedule 13.07.2010    source источник


Ответы (2)


Это болезнь новых параллельных расширений. Вы можете легко написать небезопасный код. MSDN для DataTable говорит:

Безопасность потока

Этот тип безопасен для многопоточных операций чтения. Вы должны синхронизировать любые операции записи.

Вы выполняете операцию не многопоточности в нескольких потоках. Вы должны использовать либо lock, либо SpinLock (предпочтительнее ) или ReaderWriterLockSlim.

person Andrey    schedule 13.07.2010
comment
Существует ли какая-либо структура данных, которая является потокобезопасной для операций записи? - person Iulian; 13.07.2010
comment
@Iulian stackoverflow.com/questions/2967057/ - person Andrey; 13.07.2010

Наивный подход к исправлению этого состоит в том, чтобы обернуть все в блок SyncLock.

Private Sub error_out(ByVal line As Integer, ByVal err_col As Integer, ByVal err_msg As String) 

  SyncLock t_erori
    Dim ln = t_erori.Rows.Add 
    ln.Item(0) = line 
    ln.Item(err_col) = err_msg 
    ln.Item(3) = err_col 
  End SyncLock

End Sub 

Проблема здесь в том, что вы эффективно сериализовали выполнение error_out, так как все потоки должны бороться за одну и ту же блокировку. Конечным результатом является то, что это может оказаться медленнее, чем эквивалентная непараллельная версия.

Самый простой способ воспользоваться преимуществами потоков здесь — делегировать всю операцию добавления строк в DataTable рабочему потоку и позволить основному потоку продолжить то, что он делал, а затем присоединиться к рабочему потоку к основному потоку через Thread.Join. когда основному потоку пора запросить завершение операции.

person Brian Gideon    schedule 15.07.2010