任务从Threading.Timer启动

时间:2010-07-22 21:53:48

标签: c# multithreading task

*编辑:请参阅下面的答案以获得解决方案。

以下有危险吗?我试图找出我认为可能是竞争条件的东西。我想我会从这开始并从那里开始。

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. 线程A,如果返回一行,则启动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);
    }
    
  4. 您是否认为从FastSql.ExecuteDataTable返回值可能会出现问题,因为它是静态类,然后将其与using块一起使用?

2 个答案:

答案 0 :(得分:0)

我个人要警惕在很多地方引入额外的线程 - 在这里使用线程时,“Here be Dragons”是一个有用的规则!我看不出你有什么问题,但如果它更简单,那就更容易确定。我假设您希望调用“AddToWorkQueue”在不同的线程中完成(以测试竞争条件),所以我把它留在了。

这是否符合您的需要? (眼睛编译可能是错误的)


while(true) {
    Task.Factory.StartNew( delegate { AddToWorkQueue(); });
    Thread.Sleep(5000);
}
随意放弃 - 更喜欢“扔”; “扔出去;” - 前者保留原始的调用堆栈,后者只会给你“throw ex”的行号。呼叫。更好的是,在这种情况下省略try / catch,因为您所做的只是重新抛出异常,因此您可以节省自己的尝试开销。

答案 1 :(得分:0)

事实证明这个问题非常非常奇怪。

我将原始解决方案从.NET 3.5解决方案转换为.NET 4.0解决方案。当我在全新的.NET 4.0解决方案中重新创建整个解决方案时,锁定问题就消失了。没有引入其他更改,因此我非常有信心问题是升级到4.0。