2010-07-22 3 views
0

* Редактирование: см. Мой ответ ниже для решения.Задание, запущенное с Threading.Timer

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

private BlockingCollection<MyTaskType>_MainQ = new BlockingCollection<MyTaskType>(); 
private void Start() 
{ 
    _CheckTask = new Timer(new TimerCallback(CheckTasks), null, 10, 5000); 
} 

private void CheckTasks(object state) 
{ 
    _CheckTask.Change(Timeout.Infinite, Timeout.Infinite); 
    GetTask(); 
    _CheckTask.Change(5000,5000); 
} 

private void GetTask() 
{ 
    //get task from database to object 
    Task.Factory.StartNew(delegate { 
    AddToWorkQueue(); //this adds to _MainQ which is a BlockingCollection 
    }); 
} 

private void AddToWorkQueue() 
{ 
    //do some stuff to get stuff to move 
    _MainQ.Add(dataobject); 
} 

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

код ниже:

public static void ExecuteNonQuery(string connectionString, string sql, CommandType commandType, List<FastSqlParam> paramCollection = null, int timeout = 60) 
{ 
    //Console.WriteLine("{0} [Thread {1}] called ExecuteNonQuery", DateTime.Now.ToString("HH:mm:ss:ffffff"), System.Threading.Thread.CurrentThread.ManagedThreadId); 
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    using (SqlCommand command = new SqlCommand(sql, connection)) 
    { 
    try 
    { 
     if (paramCollection != null) 
     { 
     foreach (FastSqlParam fsqlParam in paramCollection) 
     { 
      try 
      { 
      SqlParameter param = new SqlParameter(); 
      param.Direction = fsqlParam.ParamDirection; 
      param.Value = fsqlParam.ParamValue; 
      param.ParameterName = fsqlParam.ParamName; 
      param.SqlDbType = fsqlParam.ParamType; 
      command.Parameters.Add(param); 
      } 
      catch (ArgumentNullException anx) 
      { 
      throw new Exception("Parameter value was null", anx); 
      } 
      catch (InvalidCastException icx) 
      { 
      throw new Exception("Could not cast parameter value", icx); 
      } 
     } 
     } 

     connection.Open(); 
     command.CommandType = commandType; 
     command.CommandTimeout = timeout; 
     command.ExecuteNonQuery(); 

     if (paramCollection != null) 
     { 
     foreach (FastSqlParam fsqlParam in paramCollection) 
     { 
      if (fsqlParam.ParamDirection == ParameterDirection.InputOutput || fsqlParam.ParamDirection == ParameterDirection.Output) 
      try 
      { 
       fsqlParam.ParamValue = command.Parameters[fsqlParam.ParamName].Value; 
      } 
      catch (ArgumentNullException anx) 
      { 
       throw new Exception("Output parameter value was null", anx); 
      } 
      catch (InvalidCastException icx) 
      { 
       throw new Exception("Could not cast parameter value", icx); 
      } 
     } 
     } 
    } 
    catch (SqlException ex) 
    { 
     throw ex; 
    } 
    catch (ArgumentException ex) 
    { 
     throw ex; 
    } 
    } 
} 

по запросу:

FastSql.ExecuteNonQuery(connectionString, "someProc", System.Data.CommandType.StoredProcedure, new List<FastSqlParam>() { new FastSqlParam(SqlDbType.Int, "@SomeParam", variable)}); 

Кроме того, я хотел бы отметить, что этот код, кажется, не в состоянии в случайном запуске из VS2010 [Debug или Release]. Когда я делаю сборку релизов, запустите настройку на сервере-разработчике, который будет ее размещать, приложение не сработало и работает бесперебойно.

по запросу:

Текущая архитектура потоков:

  1. Нитки чтение 1 запись из таблицы планирования базы данных
  2. Thread А, если строка возвращается, запускает Task для входа в ресурс, чтобы посмотреть, есть ли файлы для передачи. Задача ссылается на объект, который содержит данные из DataTable, которые создавались с использованием статического вызова. В принципе, как показано ниже.
  3. Если файлы найдены, Task добавляет _MainQ файлы для перемещения

    //Called from Thread A 
    void ProcessTask() 
    { 
        var parameters = new List<FastSqlParam>() { new FastSqlParam(SqlDbType.Int, "@SomeParam", variable) }; 
        using (DataTable someTable = FastSql.ExecuteDataTable(connectionString, "someProc", CommandType.StoredProcedure, parameters)) 
        { 
         SomeTask task = new Task(); 
    
          //assign task some data from dt.Rows[0] 
    
          if (task != null) 
          { 
           Task.Factory.StartNew(delegate { AddFilesToQueue(task); }); 
          } 
         } 
        } 
    
    void AddFilesToQueue(Task task) 
    { 
        //connect to remote system and build collection of files to WorkItem 
        //e.g, WorkItem will have a collection of collections to transfer. We control this throttling mechanism to allow more threads to split up the work 
        _MainQ.Add(WorkItem); 
    } 
    

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

+0

Можете ли вы опубликовать фрагмент кода, который вызывает 'Take' в очереди? Также полезно будет показывать, когда вызывается вызов ExecuteNonQuery. –

+0

Извините, я был не очень ясен. Я действительно хотел увидеть код, который показывает, какой поток выполняется 'ExecuteNonQuery'. Я предполагаю, что это тот же самый поток, который называет 'Take'. Если это так, у вас есть как минимум 3 потока в игре здесь 1) тот, который вызывает 'Add' 2) сам таймер обратного вызова 3), и тот, который вызывает' Take'. Полное представление о том, как потоки взаимодействуют, является единственным верным способом определения любого возможного состояния гонки. –

+0

Нет, я не думаю, что это связано с возвратом 'FastSql.ExecuteDataTable', являющегося целью' use'. Я до сих пор не вижу, где вызывается метод BlockingCollection.Take. –

ответ

0

Оказалось, проблема была очень, очень странная.

Я преобразовал исходное решение из .NET 3.5 в решение .NET 4.0. Проблема блокировки исчезла, когда я снова создал все решение в новом .NET 4.0. Никаких других изменений не было, поэтому я очень уверен, что проблема связана с обновлением до 4.0.

0

Я лично опасаюсь вводить дополнительные потоки в довольно многих местах - «Вот Драконы» - полезное правило, когда дело касается работы с потоками! Я не вижу никаких проблем с тем, что у вас есть, но если бы это было проще, было бы легче быть более уверенным. Я предполагаю, что вы хотите, чтобы вызов «AddToWorkQueue» выполнялся в другом потоке (чтобы проверить состояние гонки), поэтому я оставил его.

Это делает то, что вам нужно? (сглаженный глаз может быть неправильным)


while(true) { 
    Task.Factory.StartNew(delegate { AddToWorkQueue(); }); 
    Thread.Sleep(5000); 
} 

случайный в стороне - предпочитают «бросить»; «выбросить»; - первый сохраняет исходный стек вызовов, последний даст вам только номер строки «throw ex»; вызов. Еще лучше, опустите try/catch в этом случае, так как все, что вы делаете, это перебросить исключения, поэтому вы можете также сэкономить накладные расходы на попытку.

+0

Да, по сути, это правда. Я предпочел бы использовать обратный вызов Threading.Timer, потому что он более эффективен и проще в CPU, чем while (true) {sleep}. Здесь есть несколько тем, которые упоминают об этом. –

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