views:

80

answers:

2

Edit: Another note: This bug locks up my entire system and kills all open connections (web browser, database connections). Basically a system wide lock up until I do "end program" on the console app. Edit: Yet another note. I am currently running the code on 3 other machines. 1 is a compiled application, the other two are running from the VS2010 IDE under Debug mode. Not one of them has crashed yet. I am starting to think this might be something unique to my system. I'm not sure where to start looking though...

Last Edit for today: I think I may have stumbled upon something... I have been using System.Threading.Timer like this

_MyTimer.Change(Timeout.Infinite, Timeout.Infinite);
//Do some stuff
_MyTimer.Change(5000,5000);

I didn't realize I was setting the re-entrant time in the second parameter. Perhaps this could be the cause of all this mess?

Is there any danger in the following? I'm trying to track down what I think might be a race condition. I figured I'd start with this and go from there.

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);
}

edit: I am also using a static class to handle writing to the database. Each call should have it's own unique data called from many threads, so it is not sharing data. Do you think this could be a source of contention?

Code below:

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;
    }
  }
}

per request:

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

Also, I wanted to note that this code seems to fail at random running it from VS2010 [Debug or Release]. When I do a release build, run setup on a dev server that will be hosting it, the application has failed to crash and has been running smoothly.

per request:

Current architecture of threads:

  1. Thread A reading 1 record from a database scheduling table
  2. Thread A, if a row is returned, launches a Task to login to resource to see if there are files to transfer. The task is referencing an object that contains data from the DataTable that was creating using a static call. Basically as below.
  3. If there are files found, Task adds to _MainQ the files to move

    //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); }

Do you think there could be a problem returning a value from FastSql.ExecuteDataTable since it is a static class and then using it with a using block?

A: 

I'd personally be wary of introducing extra threads in quite so many places - "Here be Dragons" is a useful rule when it comes to working with threads! I can't see any problems with what you have, but if it were simpler it'd be easier to be more certain. I'm assuming you want the call to "AddToWorkQueue" to be done in a different thread (to test the race condition) so I've left that in.

Does this do what you need it to? (eye compiled so may be wrong)


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

random aside - prefer "throw;" to "throw ex;" - the former preserves the original call stack, the latter will only give you the line number of the "throw ex;" call. Even better, omit the try/catch in this case as all you do is re-throw the exceptions, so you may as well save yourself the overhead of the try.

Tom Carver
Yes essentially this is true.I would rather use a Threading.Timer callback though, because it is more efficient and easier on the CPU than the while(true) { sleep }. There's a couple of threads on here that mention this.
ZeroVector
A: 

It turns out the problem was a very, very strange one.

I converted the original solution from a .NET 3.5 solution to a .NET 4.0 solution. The locking up problem went away when I re-created the entire solution in a brand new .NET 4.0 solution. No other changes were introduced, so I am very confident the problem was the upgrade to 4.0.

ZeroVector