c# - 每个循环的任务工厂带有await

标签 c# asynchronous task task-parallel-library

我是任务新手,对使用有疑问。 Task.Factory 是否会触发 foreach 循环中的所有项目或在“await”处阻塞,基本上使程序成为单线程?如果我正确地考虑了这一点,则 foreach 循环将启动所有任务和 .GetAwaiter().GetResult();阻塞主线程,直到最后一个任务完成。

另外,我只是想要一些匿名任务来加载数据。这是正确的实现吗?我不是指异常处理,因为这只是一个示例。

为了清楚起见,我正在从外部 API 将数据加载到数据库中。这个使用的是 FRED 数据库。 ( https://fred.stlouisfed.org/ ),但我有几个我会点击来完成整个传输(可能是 200k 数据点)。一旦完成,我就会更新表格、刷新市场计算等。其中一些是实时的,一些是日终的。我还想说,我目前在 docker 中一切正常,但一直在努力使用任务来更新代码以提高执行力。

class Program
{
    private async Task SQLBulkLoader() 
    {

        foreach (var fileListObj in indicators.file_list)
        {
            await Task.Factory.StartNew(  () =>
            {

                string json = this.GET(//API call);

                SeriesObject obj = JsonConvert.DeserializeObject<SeriesObject>(json);

                DataTable dataTableConversion = ConvertToDataTable(obj.observations);
                dataTableConversion.TableName = fileListObj.series_id;

                using (SqlConnection dbConnection = new SqlConnection("SQL Connection"))
                {
                    dbConnection.Open();
                    using (SqlBulkCopy s = new SqlBulkCopy(dbConnection))
                    {
                      s.DestinationTableName = dataTableConversion.TableName;
                      foreach (var column in dataTableConversion.Columns)
                          s.ColumnMappings.Add(column.ToString(), column.ToString());
                      s.WriteToServer(dataTableConversion);
                    }

                  Console.WriteLine("File: {0} Complete", fileListObj.series_id);
                }
             });
        }            
    }

    static void Main(string[] args)
    {
        Program worker = new Program();
        worker.SQLBulkLoader().GetAwaiter().GetResult();
    }
}

最佳答案

您等待从 Task.Factory.StartNew 返回的任务确实使其有效地成为单线程。您可以通过这个简短的 LinqPad 示例看到对此的简单演示:

for (var i = 0; i < 3; i++)
{
    var index = i;
    $"{index} inline".Dump();
    await Task.Run(() =>
    {
        Thread.Sleep((3 - index) * 1000);
        $"{index} in thread".Dump();
    });
}

随着循环的进行,我们等待的时间会减少。输出为:

0 inline
0 in thread
1 inline
1 in thread
2 inline
2 in thread

如果您删除 StartNew 前面的 await,您将看到它并行运行。正如其他人提到的,您当然可以使用 Parallel.ForEach,但是为了演示手动执行此操作,您可以考虑这样的解决方案:

var tasks = new List<Task>();

for (var i = 0; i < 3; i++) 
{
    var index = i;
    $"{index} inline".Dump();
    tasks.Add(Task.Factory.StartNew(() =>
    {
        Thread.Sleep((3 - index) * 1000);
        $"{index} in thread".Dump();
    }));
}

Task.WaitAll(tasks.ToArray());

现在请注意结果如何:

0 inline
1 inline
2 inline
2 in thread
1 in thread
0 in thread

关于c# - 每个循环的任务工厂带有await,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53981438/

相关文章:

c# - 将实现哪个接口(interface)?

Django 异步请求

javascript - 如何异步更改数据数组项、添加和切片

gradle - 如何合并不同任务的相同代码

c# - 从 Lambda 表达式中获取包含参数值的对象数组

c# - vs2010循环依赖问题

c# - 随机抛出 "Could not establish trust relationship for the SSL/TLS secure channel"

c# - 异步方法不返回 asp.net mvc 4

c++ - 线程构建 block 生成嵌套任务

c# - 在任务中使用异步套接字