SQL-запрос в Parallel.ForEach - тупик

У меня есть статический метод, который вызывает SQL SP внутри статического метода в цикле Parallel.ForEach. SP вставляет данные в 3 разные таблицы. Я использую уровень сериализуемых транзакций. Но время от времени я попадаю в тупиковую ситуацию.

Я думаю, что если я сделаю этот метод методом экземпляра или воспользуюсь простым ForEach, это может решить проблему.

Я правильно думаю? Мне тоже нужно заблокировать список?

--Код--

Parallel.ForEach(MyConcurrentDictionary, MyElement =>
{
   if (MyElement.SomeProperty != SomeValue)
      {
         PublishMessage(MyElement);
      }
      else
      {
         InsertInDatabase(MyElement);

      }
}




public static void InsertInDatabase()
{
   DataTable t1 = new DataTable();
   DataTable t2 = new DataTable();
   DataTable t3 = new DataTable();


   CreateTable(T1);
   CreateTable(T2);
   CreateTable(T3);

   using (var conn = new SqlConnection(ConnString))
      {
         try
            {
               conn.Open();

               // Begin transaction
               using (SqlTransaction transaction = conn.BeginTransaction(IsolationLevel.Serializable))
                  {
                     SqlCommand cmd = new SqlCommand();
                     cmd.Transaction = transaction;
                     cmd.Connection = conn;
                     cmd.CommandType = CommandType.StoredProcedure;
                     cmd.CommandText = "SPName";
                     cmd.Parameters.AddWithValue("@T1", T1);
                     cmd.Parameters.AddWithValue("@T2", T2);
                     cmd.Parameters.AddWithValue("@T3", T3);
                     cmd.ExecuteNonQuery();

                     transaction.Commit();

                }
           }

     }
}

person Asdfg    schedule 01.06.2011    source источник
comment
Вы запускаете все это в одной транзакции?   -  person Johann Blais    schedule 01.06.2011
comment
@Johann Blais - Каждый элемент создает свою собственную транзакцию. Транзакция создается внутри Parallel.ForEach.   -  person Asdfg    schedule 01.06.2011
comment
Возможно, это просто не стоит многопоточности. Если вы выполняете вставки SQL, вы, вероятно, будете больше связаны с вводом-выводом, чем с процессором.   -  person fire.eagle    schedule 01.06.2011
comment
Вам действительно нужна изоляция Serializable? рассмотрите возможность перехода к «Read Committed» (значение по умолчанию для SQL Server).   -  person Polyfun    schedule 01.06.2011
comment
@ShellShock - У меня раньше было Read Commited, но он переходил в ситуацию тупика, поэтому я изменил его на Serializable.   -  person Asdfg    schedule 01.06.2011
comment
У вас есть код, который вы можете опубликовать?   -  person Brian Gideon    schedule 01.06.2011
comment
Есть ли у SP возможность вызвать тупик?   -  person SWeko    schedule 01.06.2011
comment
@Brian Gideon - Отредактировал вопрос с помощью кода   -  person Asdfg    schedule 01.06.2011
comment
Также будет полезен код в SP. Скорее всего, именно в этом и найдете причину тупика.   -  person Mikael Eriksson    schedule 01.06.2011
comment
IsolationLevel.Serializable ЗЛО !!! Это наиболее требовательная настройка блокировки, которую вы можете получить. Вы уверены, что хотите этого? Я сомневаюсь, что вы это сделаете ... Serializable в Sql Server очень быстро перерастет блокировки в блокировки диапазона и таблицы. Если у вас есть какая-то пропускная способность, обязательно возникнут тупиковые ситуации. ReadCommitted - это то, что вы ищете, и это уровень изоляции по умолчанию на сервере Sql, Serializable - это уровень по умолчанию в .Net.   -  person Didaxis    schedule 07.10.2013


Ответы (2)


Попробуйте изменить свои классы на нестатические методы, которые очень помогут. НИКОГДА не используйте статику параллельно, это просто напрасно. А в целях безопасности попробуйте использовать список сохранения потоков, также известный как синхронизированный массив или System.Collections.Concurrent.ConcurrentQueue(Of T).

С уважением

person orophine    schedule 04.06.2012

Проблемы возникают из-за того, что вы не используете синхронизацию. Ключевое слово static не означает, что это потокобезопасный. Несколько потоков из Parallel.ForEach по-прежнему могут обращаться к этому методу одновременно.

У вас есть много вариантов для синхронизации. Но я начну с самого простого - сделать его однопоточным. Если это невозможно, используйте lock или просмотрите другие варианты

ОБНОВЛЕНИЕ
Как заметил @Colin, мой ответ работает, если вы где-то обновляете данные. Если все доступно только для чтения, значит, есть еще кое-что. Проверьте создание DataTable.

person oleksii    schedule 01.06.2011
comment
Не обязательно. Не показано ничего, что могло бы вызвать проблемы синхронизации потоков в коде C #. (Однако отсутствует код, который может вызывать проблемы). Совершенно нормально вызывать статический метод одновременно, если он не обменивается данными с другими потоками (или любые данные, которые используются совместно, только читаются только во всех потоках) - person Colin Mackay; 01.06.2011
comment
@Colin Я предположил, что создание DataTable, скорее всего, будет небезопасным для потоков. - person oleksii; 02.06.2011
comment
Каждый метод статичен. Я ненавижу, когда люди так делают, когда имеет смысл создавать методы экземпляра метода. Это не служебные методы. Эти методы выполняют операции с базой данных. - person Asdfg; 02.06.2011
comment
@Asdfg, можете ли вы реорганизовать это до методов экземпляра, или это слишком много работы? - person oleksii; 02.06.2011
comment
Это то, что я собираюсь делать дальше. - person Asdfg; 02.06.2011