无法转换为System.Threading.Tasks.Task&#39; to&#39; System.Collections.Generic.Dictionary <string,string>&#39; </string,string>

时间:2014-12-25 05:52:13

标签: c# .net multithreading task-parallel-library async-await

我相信我可能只是语法错误,但我想要做的是创建一个在另一个任务完成后运行的任务。

我在列表中为每个100的数组创建了一个任务。它启动一个新线程将该数组传递给一个方法。该方法在完成时返回字典。我尝试创建一个在方法完成后运行的任务,它将返回的字典传递给另一个执行更多工作的方法。

static void Main(string[] args)
    {
        try
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            while (true)
            {
                startDownload();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

public static async void startDownload()
    {
        try
        {

            DateTime currentDay = DateTime.Now;

            if (Helper.holidays.Contains(currentDay) == false)
            {
                List<string> markets = new List<string>() { "amex", "global", "nasdaq", "nyse" };

                Parallel.ForEach(markets, async market =>
                {
                    try
                    {

                        IEnumerable<string> symbolList = Helper.getStockSymbols(market);
                        var historicalGroups = symbolList.Select((x, i) => new { x, i })
                          .GroupBy(x => x.i / 100)
                          .Select(g => g.Select(x => x.x).ToArray());

                        Task<Dictionary<string, string>>[] historicalTasks =
                                                           historicalGroups.Select(x => Task.Run(() =>
                                                           Downloads.getHistoricalStockData(x, market)))
                                                                    .ToArray();

                        Dictionary<string, string>[] historcalStockResults = await
                                                                             Task.WhenAll(historicalTasks);

                        foreach (var dictionary in historcalStockResults)
                        {
                            Downloads.updateSymbolsInDB(dictionary);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                });

                await Task.Delay(TimeSpan.FromHours(24));
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

2 个答案:

答案 0 :(得分:5)

如果您已使用ContinueWith,我建议您不要使用await。原因是你在代码中得到的冗长。

相反,尽可能使用await。 代码最终如下:

var historicalGroups = symbolList
                       .Select((x, i) => new { x, i })
                       .GroupBy(x => x.i / 100)
                       .Select(g => g.Select(x => x.x).ToArray());

var historicalTasks = historicalGroups.Select(x => Task.Run(() => 
                                       Downloads.getHistoricalStockData(x, market)))
                                      .ToArray();

var historcalStockResults = await Task.WhenAll(historicalTasks);

foreach (var dictionary in historcalStockResults)
{
    Downloads.updateSymbolsInDB(dictionary);
}

请注意使用Task.Run代替Task.Factory.StartNew。你应该使用它。有关here

的更多信息

修改

如果您需要每24小时执行一次此代码,请在其上添加Task.Delayawait

await Task.Delay(TimeSpan.FromHours(24));

编辑2:

您的代码无效的原因是因为startDownloadasync void,而您还没有等待它。因此,无论您的while是什么,Task.Delay循环都会继续迭代。

由于您位于控制台应用程序中,因此await方法无法异步,因此您无法Main。因此,要解决此问题,请将startDownload更改为async Task而不是async void,并将Wait更改为Task。请注意,使用Wait 几乎永远不会被使用,期望特殊情况(例如在控制台应用程序内运行的情况):

public async Task StartDownload()

然后

while (true)
{
    StartDownload().Wait();
}

另请注意,混合Parallel.Foreachasync-await并不总是最好的主意。您可以在Nesting await in Parallel.ForEach

中详细了解相关内容

答案 1 :(得分:2)

您会看到ContinueWith将任务视为任务。

此外,通过我们的评论中的逻辑,看起来您需要更新代码。您尝试在所有Downloads.updateSymbolsInDB任务完成的结果上运行WhenAll一次。在您的情况下,它看起来像你需要

await Task<Dictionary<string, string>>
    .WhenAll(historicalGroups.Select(g => Task.Factory.StartNew(() => Downloads.getHistoricalStockData(g, market))))
    .ContinueWith((i) => Downloads.updateSymbolsInDB(i.Result.Aggregate((agg, next) =>
            {
                foreach (var p in next)
                {
                    if (!agg.ContainsKey(p.Key)) { agg.Add(p.Key, p.Value); }
                }
                return agg;
            })));

请注意,我还使用了Task<TResult>来启动,因此ContinueWith的排版方式很强,可以提供Dictionary<string, string> i.Result

另请注意,您需要重新审视我在i.Result.Aggregate逻辑中所做的事情。您需要更新它以使其适合您的情况。此外,可能值得重新审视一下,查看Downloads.updateSymbolsInDB多次拨打电话是否更有效,或者Aggregate电话是否是更好的选择。

(最后注意:最终结果为void,因此未分配await。)

在审核中,您的代码存在许多问题(意味着声明,而不是指控 - 没有违法行为)。您通过Task.Factory.StartNewWhenAll方法创建的任务没有返回值,因此只是基本的Task对象,实际上丢弃了Downloads.getHistoricalStockData工作的结果。然后,您的ContinueWith需要使用Task<TResult>的{​​{1}},其中WhenAll将是TResult中每个任务的返回值数组。

编辑据我所知,如果WhenAll多次被调用,代码如何更新,可以简单地完成:

Downloads.updateSymbolsInDB