I believe I might just have the syntax wrong but what I'm trying to do is create a task that runs after another task is finished.
I have a task for each array of 100 in a list. It starts a new thread passing that array into a method. The method returns a dictionary when it finishes. I'm trying to create a task to run after the method is finishes where it passes the returned dictionary to a separate method that does some more work.
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);
}
}
I would advise not to use ContinueWith
at all if you're already using await
. The reason is the verbosity you end up with in your code.
Instead, use await
where possible. The code ends up like this:
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);
}
Note the use of Task.Run
instead of Task.Factory.StartNew
. You should use that instead. More on that here
Edit:
If you need to execute this code once every 24 hours, add a Task.Delay
and await
on it :
await Task.Delay(TimeSpan.FromHours(24));
Edit 2:
The reason your code isn't working is because startDownload
is async void
, and you're not awaiting on it. Thus your while
loop keeps iterating regardless of your Task.Delay
.
Because you're inside a Console Application, you can't await
because Main
methods cant be async. So to work around that, change startDownload
to be async Task
instead of async void
, and Wait
on that returned Task
. Do note that using Wait
should almost never be used, expect for special scenarios (such as the one while running inside a console app):
public async Task StartDownload()
and then
while (true)
{
StartDownload().Wait();
}
Also note that mixing Parallel.Foreach
and async-await
isn't always the best idea. You can read more on that in Nesting await in Parallel.ForEach
You'll see that ContinueWith takes as Task as argument.
Also, from working through your logic in our comments, it looks like you'll need to update your code. You are trying to run the Downloads.updateSymbolsInDB
once on the result of all WhenAll
tasks are completed. In your case it looks like you'll need
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;
})));
Note that I also used the Task<TResult>
to initiate, so that the ContinueWith
is strongly typed to provide the Dictionary<string, string>
i.Result
.
Also note that you'll want to revisit what I'm doing in the i.Result.Aggregate
logic. You'll need to update that to be correct to your case. Additionally, it may be worth revisiting to see if it is more efficient to just call Downloads.updateSymbolsInDB
multiple times, or if the Aggregate
call is the better choice.
(Last note: the end result is a void
, so the await isn't assigned.)
In review, there were a number of problems with your code (meant as a statement, not an accusation - no offense intended). Your tasks created via Task.Factory.StartNew
for your WhenAll
method had no return value, thus were just basic Task
objects, and were in fact throwing away the results of the Downloads.getHistoricalStockData
work. Then, your ContinueWith
needed to work with the Task<TResult>
of the WhenAll
, where TResult
will be an array of the return value of each task within WhenAll
.
EDIT: As was asked how the code could be updated if Downloads.updateSymbolsInDB
were to be called multiple times, it could simply be done as:
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());
Parallel.ForEach(historicalGroups, g => Downloads.updateSymbolsInDB(Downloads.getHistoricalStockData(g, market)));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.