Является ли вызов подпрограммы внутри Parallel.For (и передача ей переменной ByRef) потокобезопасным?

Я вызываю подпрограмму MyPartsMatrix внутри вложенных циклов Parallel.For (vb.net). MyPartsMatrix требуется переменная с именем «unfilled», которая передается ByRef, поскольку это значение изменяется внутри подпрограммы MyPartsMatrix. Мне нужно получить и сохранить это значение после выполнения подпрограммы MyPartsMatrix.

«Незаполненная» переменная дает другое значение, когда я запускаю параллельную версию этого кода по сравнению с непараллельной версией, использующей обычные вложенные циклы For...Next. Я не могу понять, почему это так.

Является ли потокобезопасным вызов другой подпрограммы из цикла Parallel.For?

Является ли эта переменная «незаполненной» потокобезопасной?

    Dim ConcurrentListofResults As ConcurrentQueue(Of FindBestResults)
    ConcurrentListofResults = New ConcurrentQueue(Of FindBestResults)

    Dim x = 5, y = 5

    Parallel.For(0, x, Sub(oD)
                           Parallel.For(0, y, Sub(oT)

                                                  Dim unfilled As Integer = 0
                                                  MyPartsMatrix (oD, oT, unfilled)

                                                  'Create a FBS item to add to the concurrent list collection
                                                  Dim FBSResultsItem = New FindBestResults
                                                  FBSResultsItem.oD = oD
                                                  FBSResultsItem.oT = oT
                                                  FBSResultsItem.unfilled = unfilled

                                                  'Add this item to the Concurent collection
                                                  ConcurrentListofResults.Enqueue(FBSResultsItem)

                                              End Sub)
                       End Sub)
    'Get best result.
    Dim bestResult As FindBestResults
    For Each item As FindBestResults In ConcurrentListofResults
        If item.unfilled < bestResult.unfilled Then
            bestResult.oD = item.oD
            bestResult.oT = item.oT
            bestResult.unfilled = item.unfilled
        End If
    Next

    Public Sub MyPartsMatrix (ByVal oD As Integer, ByVal oT As Integer, ByRef unfilled As Integer)

      '....do stuff with the unfilled variable....
      'unfilled is a counter that is incremented while we run through the PartsMatrix
       unfilled = unfilled + 1  
    End Sub

Если это не потокобезопасно, есть ли другой способ написать это, чтобы «незаполненная» переменная была потокобезопасной или чтобы сделать вызов другого потока подпрограммы безопасным?


person Jeff    schedule 15.09.2016    source источник


Ответы (1)


Без определения MakeSchedule (которое вы назвали MyMakePartsMatrix в другом месте) невозможно сказать, является ли он потокобезопасным.

Чтобы быть потокобезопасным, подпрограмма не должна изменять ничего, кроме незаполненного. Это гарантирует, что многократный вызов с одними и теми же входными данными всегда будет давать одни и те же выходные данные. Я бы также рекомендовал преобразовать в функцию, так как мне намного легче понять, что происходит.

В другой заметке:

Ваша производительность будет лучше, если вы не будете вкладывать параллельные циклы. В настоящее время вы ожидаете завершения своего внутреннего цикла, прежде чем запускать второй цикл. Если вы используете большие значения x + y, то что-то похожее на код будет работать лучше.

    Dim scenarios = From x2 In Enumerable.Range(0, x)
        From y2 In Enumerable.Range(0, y)
        Select New With {x2, y2}

    Parallel.ForEach(scenarios, Sub(s)

                                End Sub)
person FloatingKiwi    schedule 15.09.2016
comment
Извините, я исправил несоответствия в своем примере кода. Я не хотел вставлять туда все это целиком, поэтому я попытался упростить все для удобочитаемости. Незаполненная переменная — это, по сути, счетчик, который увеличивается внутри подпрограммы MyPartsMatrix. Вы упомянули о рассмотрении функции - вы имеете в виду изменение MyPartsMatrix из Sub в функцию (возвращение незаполненного) или мне следует изменить лямбда-раздел из Sub в функцию? Все еще новичок в концепции parallel.for, поэтому я на самом деле не совсем уверен, в чем разница между этими двумя. Спасибо за предложение не вкладывать циклы! - person Jeff; 16.09.2016
comment
Кроме того, код, создающий коллекцию сценариев, выглядит отличной идеей! Итак, это создает набор элементов, которые охватывают (0,0) до (x, y)? Если да, то как мне получить доступ к значениям x2 и y2 внутри оператора parallel.foreach. Могу ли я просто использовать переменные x2 и y2, потому что их нужно передать в подпрограмму MyPartsMatrix. Наконец, используется ли LINQ? - person Jeff; 16.09.2016
comment
Если вы используете сабвуфер с одним выходным параметром, я обычно меняю его на функцию, просто мое личное предпочтение, это на самом деле не меняет то, как все работает. Да, это линк. Чтобы получить доступ к переменной внутри ForEach, используйте s.x2 и s.y2. В этом примере unfilled всегда будет 1, когда он будет возвращен, поэтому я подозреваю, что вы делаете что-то более активное. Имейте в виду, однако, что если ваша подпрограмма изменяет что-либо, кроме unfilled, oD или oT, то она, вероятно, не является потокобезопасной. Если вы опубликуете фактический код, я могу помочь вам решить проблему. - person FloatingKiwi; 16.09.2016