异步SQL查询执行 - Task.WaitAll(tasks)永远不会完成

dr3*_*r3x 2 c# asynchronous async-await

我有一个应用程序,使用异步查询执行将数据从MS SQL服务器移动到MySQL服务器; 数据移动正常,但RunAllTask​​s()方法中的Task.WaitAll(任务)调用永远不会完成.

异步任务都遵循PumpLocsAsync()的模式,其中通过BeginExecuteReader异步调用对MS SQL的调用; 当读者返回结果时,MySQL会正常插入.

..

async Task PumpLocsAsync()
{
     using (var conn = new SqlConnection(SqlConnStr))
     using (var cn = new MySqlConnection(MysqlConnStr))
     using (var cmd = new SqlCommand())
     {
          cmd.Connection = conn;
          cmd.CommandText = "SELECT HERE";

          conn.Open();
          var handle = cmd.BeginExecuteReader();
          await Task.Factory.FromAsync(handle, (ar) =>
          {
               var rdr = cmd.EndExecuteReader(ar);
               var qry = @"INSERT HERE";

               cn.Open();
               using (var cmdm = new MySqlCommand(qry, cn))
               {
                   cmdm.CommandTimeout = MysqlCmdtimeout;
                   <PARAM SETUP HERE>
                   <COLUMN MAPPING HERE>

                   while (RetryUtility.RetryMethod<bool>(() => rdr.Read(), 3, 1000))
                   {
                       <LOADING PARAMS WITH BITS HERE>
                       RetryUtility.RetryAction(() => cmdm.ExecuteNonQuery(), 3, 1000);
                   }
               }
               Console.WriteLine("Finished Locs!");
               cn.Close();
          });
          conn.Close();
    }
}


...

        void RunAllTasks()
        {
            Task[] tasks = { PumpLocsAsync(), PumpPicsAsync() };

            try
            {
                Task.WaitAll(tasks);
                Console.WriteLine("Finished with all tasks...");
                foreach (var task in tasks)
                {
                    Console.WriteLine("Id: {0}, Status: {1}", task.Id, task.Status);
                }
            }

....
Run Code Online (Sandbox Code Playgroud)

svi*_*ick 7

你误解了如何调用Task.Factory.FromAsync(),你传递BeginXxx方法的结果和方法的委托EndXxx:

var rdr = await Task.Factory.FromAsync(
    cmd.BeginExecuteReader,
    (Func<IAsyncResult, SqlDataReader>)cmd.EndExecuteReader,
    null);
Run Code Online (Sandbox Code Playgroud)

如果您想在操作完成后执行某些操作,只需将其放在此行下方,await将确保它在正确的时间执行.

  • 只要有可能,我建议将委托传递给`Begin*`方法而不是`Begin*`方法的结果.如果它可以将整个Begin/End事务包装成`Task`,那么FromAsync`会更有效.如果它只有一个`IAsyncResult`,那么它必须在`AsyncWaitHandle`(通常是懒惰创建的)上调用`ThreadPool.RegisterWaitForSingleObject`(并且你在app域中只能有64个).OTOH,如果它有两个委托,那么它可以为`Begin*`方法提供自己的`AsyncCallback`,而`TaskCompletionSource`包装器非常简单. (2认同)