The following code is used to simulate the time-consuming works.
async Task DoWork(string n)
{
for (var i = 1; i <= 5; i++)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
}
}
async Task<int> F1()
{
await DoWork("f1");
return 1;
}
async Task<string> F2()
{
await DoWork("f2");
return "X";
}
The following code will run F1() and F2() sequentially.
async Task<string> Main()
{
var f1Task = F1();
var f2Task = F2();
var f1 = await f1Task;
var f2 = await f2Task;
return $"{f1} {f2}";
}
await Main();
The following code can make them run in parallel. However, it looks cumbersome.
async Task<string> Main2()
{
int f1 = 0;
async Task G1() { f1 = await F1(); }
string f2 = "";
async Task G2() { f2 = await F2(); }
await Task.WhenAll(Task.Run(() => G1()), Task.Run(() => G2()));
return $"{f1} {f2}";
}
await Main2();
Is there a way to do it without wrapping them in Task.Run()?
You can use WhenAll() method to have your task run parallel like
async Task<int> Main()
{
var f1Task = F1();
var f2Task = F2();
await Task.WhenAll(f1Task,f2Task);
return await f1Task + await f2Task;
}
await itself doesn't start the asynchronous operation.
Kick off DoWork with a Task like this:
async Task DoWork(string n)
{
await Task.Run(() => {
for (var i = 1; i <= 5; i++)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
}
});
}
Then you cando this:
{
var f1Task = F1();
var f2Task = F2();
return await f1Task + await f2Task;
}
You're right that it looks cumbersome. However, it can be done in a neat way. You can create a tasks array and then use WhenAll to wait for all of them at once.
var tasks = new List<Task<int>> { F1(), F2() };
var result = Task.WhenAll(tasks).GetAwaiter().GetResult();
The only condition is that all tasks has to return the same result type. Then result in this case is array of int.
I wrote a nice article about parallel processing, you can have a look at how this can be accomplished when a task is a REST call: http://www.michalbialecki.com/2018/04/19/how-to-send-many-requests-in-parallel-in-asp-net-core/
It will be both io and CPU heavy.
If you have just I/O (i.e., await Task.Delay instead of Thread.Sleep), then you can use asynchronous concurrency:
var task1 = F1();
var task2 = F2();
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;
However, since you have CPU as well (and since your method is synchronous), you'll need to push that to another thread and make it parallel.
Is there a way to do it without wrapping them in Task.Run()?
Yes, but Task.Run is the simplest solution here. It can look less cumbersome than your example:
var task1 = Task.Run(() => F1());
var task2 = Task.Run(() => F2());
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;
Technically, the Task.WhenAll is optional in both of these examples, but I like it since it makes the semantics more clear.
Related
I found many questions addressing how to sequence tasks and waiting until all tasks finish, but with this topic, I found only 1 question from 2016 with no answers.
I'm processing a large text file in my project and I want to indicate that this process is running with the text being displayed with changing number of dots after the "Processing" text. I got to the point, where the intended looping task is working until a long working task finishes and the proper field in the VM is updated, but I can't make looping task to be delayed so dots are changing in the way it's seen.
In other words - the same functionality as when a loader is displayed while data are being retrieved from the HTTP request.
public void SetRawTextFromAbsPath(string path)
{
if (!File.Exists(path))
{
return;
}
var rawText = "Processing";
bool IsReadingFileFinished = false;
Task<string> getRawTextFromAbsPath = Task.Run(() => {
var result = FileProcessingServices.GetRawFileText(path);
IsReadingFileFinished = true;
return result;
});
Task updateProgressText = Task.Run(async () =>
{
while (!IsReadingFileFinished)
{
rawText = await Task.Run(() => ProcessingTextChange(rawText));
SelectedFileRaw = rawText;
}
});
Task.WaitAll(getRawTextFromAbsPath, updateProgressText);
SelectedFileRaw = completeRawText.Result;
}
public string ProcessingTextChange(string text)
{
Task.Delay(100);
var dotsCount = text.Count<char>(ch => ch == '.');
return dotsCount < 6 ? text + "." : text.Replace(".", "");
}
After learning from all the answers, I come up with this solution:
private const string PROGRESS = "Progress";
private const int PROGRESS_DELAY = 200;
public async void RunProgressTextUpdate()
{
var cts = new CancellationTokenSource();
if (!IsRunning)
{
UpdateProgressTextTask(cts.Token);
string longTaskText = await Task.Run(() => LongTask(cts));
await Task.Delay(PROGRESS_DELAY);
ProgressText = longTaskText;
}
}
private void UpdateProgressTextTask(CancellationToken token)
{
Task.Run(async () =>
{
ProgressText = PROGRESS;
while (!token.IsCancellationRequested)
{
await Task.Delay(PROGRESS_DELAY);
var dotsCount = ProgressText.Count<char>(ch => ch == '.');
ProgressText = dotsCount < 6 ? ProgressText + "." : ProgressText.Replace(".", "");
}
});
}
private string LongTask(CancellationTokenSource cts)
{
var result = Task.Run(async () =>
{
await Task.Delay(5000);
cts.Cancel();
return "Long task finished.";
});
return result.Result;
}
Every way of creating Task and running them is overloaded to expect a CancellationToken. CancellationTokens are, unsurprinsignly, structs that allows us to cancel Tasks.
Having this two methods
public void DelayedWork()
{
Task.Run(async () =>
{
// Simulate some async work
await Task.Delay(1000);
});
}
public void LoopingUntilDelayedWorkFinishes()
{
Task.Run(() =>
{
int i = 0;
// We keep looping until the Token is not cancelled
while (true) // May be?
{
Console.WriteLine($"{++i} iteration ...");
}
});
}
We want LoopingUntilDelayedWorkFinishes to stop looping when DelayedWork finishes (well, naming was quite obvious).
We can provide a CancellationToken to our LoopingUntilDelayedWorkFinishes method. So it will keep looping until it is cancelled.
public void LoopingUntilDelayedWorkFinishes(CancellationToken token)
{
Task.Run(() =>
{
int i = 0;
// We keep looping until the Token is not cancelled
while (!token.IsCancellationRequested)
{
Console.WriteLine($"{++i} iteration ...");
}
}, token); // This is the overload expecting the Token
}
Okay, working. We can control this CancellationToken by extracting from a CancellationTokenSource, which controls its CancellationToken.
var cts = new CancellationTokenSource();
p.LoopingUntilDelayedWorkFinishes(cts.Token);
And now, we need our DelayedWork to cancel the token when it finishes.
public void DelayedWork(CancellationTokenSource cts)
{
Task.Run(async () =>
{
// Simulate some async work
await Task.Delay(1000);
// Once it is done, we cancel.
cts.Cancel();
});
}
That is how we could call the methods.
var cts = new CancellationTokenSource();
p.DelayedWork(cts);
p.LoopingUntilDelayedWorkFinishes(cts.Token);
The call order between DelayedWork and LoopingUntilDelayedWorkFinishes is not that important (in that case).
Maybe LoopingUntilDelayedWorkFinishes can return a Task and the await for it later on, I don't know. I just depends on our needs.
There are tons of ways to achieve this. The environment arround Task is so bast and the API is quite confusing sometimes.
Here's how you could do it. Maybe some smart use of async/await syntax would improve the solution I gave. But, here's the main idea.
Hope it helps.
I need to run multiple task to access to database and to wait to all task finished to obtain the result and assign it to my viewmodel, i try many sample but i have never have the result, any help please, this my code
var model = new AggregationViewModel();
var loadDataTasks = new Task[]
{
Task.Run(async () =>model.Alive = await _repository.GetCountAsync<filter>(Predicate)),
Task.Run(async () => model.Delay = await _repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused")))
};
try
{
await Task.WhenAll(loadDataTasks);
foreach (var item in loadDataTasks)
{
}
}
catch (Exception ex)
{
}
Check this out:
var task1 = _repository.GetCountAsync<filter>(Predicate);
var task2 = _repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused"));
await Task.WhenAll(task1, task2); //wait both tasks to finish
model.Alive = await task1;
model.Delay = await task2;
PS: the function, which the above code is placed, is needed to be async for the above scenario.
Try this code:
var model = new AggregationViewModel();
//----------------------------------------
var tasks = new List<Task<int>>();
//Add CountA
tasks.Add(_repository.GetCountAsync<filter>(Predicate));
//Add CountB
tasks.Add(_repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused")));
//----------------------------------------
// Create a task with "allTasks" name to wait to complete all tasks
Task allTasks = Task.WhenAll(tasks);
try {
// Wait to compelete "allTask"
allTasks.Wait();
}
catch(AggregateException)
{}
//----------------------------------------
//Working on the results
if (allTasks.Status == TaskStatus.RanToCompletion) {
model.CountA=allTasks.Result[0]
model.CountB=allTasks.Result[1]
}
// Display information on faulted tasks.
else {
foreach (var t in tasks) {
Console.WriteLine("Task {0}: {1}", t.Id, t.Status);
}
}
Let's say I have more Uri's. I need to validate, if they are reachable.
public RelayCommand TestConnectionCommand => new RelayCommand(async () =>
{
var res1 = await ValidateUriAsync(uri);
var res2 = await ValidateUriAsync(uri);
});
private async Task<bool> ValidateUriAsync(Uri uri)
{
try
{
var request = WebRequest.CreateHttp(uri);
var result = await request.GetResponseAsync();
return true;
}
catch (Exception e)
{
return false;
}
}
When the program comes to first await it takes some time to validate the uri, after I get the result, I can show the result on the View. Then program goes next and I validate second uri. I'd like to do that parallel, without awaiting. I was thinking about using Begin/EndGetResponse. I need to show the result for each validation on the View. Validation succeeded/failed.
Many thanks for advice.
When using await you stop the execution until the task returns, instead wait for all task to finish:
var task1 = ValidateUriAsync(uri);
var task2 = ValidateUriAsync(uri);
await Task.WhenAll(task1, task2);
or to wait until the first fault:
var tasks = new List<Task>
{
ValidateUriAsync(), ValidateUriAsync(uri)
};
while (tasks.Any())
{
var t = await Task.WhenAny(tasks);
if (t.IsFaulted)
{
//Faulty
break;
}
tasks.Remove(t);
}
I have following code:
List<Task<int>> taskArray = new List<Task<int>>();
for (int i = 0; i < 100; i++)
{
taskArray.Add(myCrawler.getWebPageCharsCount("https://www.something.com"));
}
The method looks like this:
public Task<int> getWebPageCharCount(string url)
{
var client = new HttpClient();
return Task.Run(async () =>
{
Task<string> task = client.GetStringAsync(url);
string taskResult = await task;
return taskResult.Length;
});
}
After this, there is a 100 threads running, what I want to achive is to process the result in main thread after each individual task is done, not to wait to all results, which would I do with the following code:
var results = await Task.WhenAll(taskArray);
foreach (var res in results)
{
myTextBox.Text += res.ToString() + "\n";
}
I was thinking about something like this:
foreach (var task in taskArray)
{
var result = await task;
myTextBox.Text += result.ToString() + "\n";
}
But after testing and reading about await in loops, I know it's running synchronized. Is there a way to process the results continuously in main thread?
There are several solutions to this, but the simplest is to use await Task.WhenAny(), which returns a completed Task, which you then process and remove from the list of tasks, then repeat the same until the list is empty.
Example as requested:
List<Task<int>> indexingArray = new List<Task<int>>(taskArray);
var results = new int[taskArray.Count];
while (taskArray.Any())
{
Task<int> completedTask = await Task.WhenAny(taskArray).ConfigureAwait(false);
results[indexingArray.IndexOf(completedTask)] = completedTask.Result;
taskArray.Remove(completedTask);
}
myTextBox.Text = string.Join("\n", results);
(I added an indexing collection to get the correct index for the results. There are probably better ways to do that but as the question is about tasks I'll leave it as-is).
what I want to achive is to process the result in main thread after each individual task is done, not to wait to all results
The simplest solution is to introduce an async method that corresponds to that "process" concept. You already have an async way of "retrieval", you just need to introduce an async operation of "retrieve and process":
// (getWebPageCharCount is unchanged)
private Task GetAndDisplayWebPageCharCount(string url)
{
var res = await myCrawler.getWebPageCharCount(url);
myTextBox.Text += res.ToString() + "\n";
}
Used as such:
List<Task> taskArray = new List<Task>();
for (int i = 0; i < 100; i++)
taskArray.Add(GetAndDisplayWebPageCharCount("https://www.something.com"));
await Task.WhenAll(taskArray);
On a side note, there's abosolutely no need for Task.Run wrapping HttpClient.GetStringAsync. Also, the "100 threads running" remark is incorrect; there are 100 tasks in progress.
I have Task, Task1 and Task2. Task1 and Task2 are independent from each other, but both depend on the result of Task. I can make it work this way:
static async Task Test1()
{
var task = Task.Delay(1000);
var task1 = task.ContinueWith(_ =>
{
Console.WriteLine("Task1, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
var task2 = task.ContinueWith(_ =>
{
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
await Task.WhenAll(task1, task2);
}
Or, alternatively, this way:
static async Task Test2()
{
var task = Task.Delay(1000);
await task;
var task1 = Task.Run(() =>
{
Console.WriteLine("Task1, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
var task2 = Task.Run(() =>
{
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
await Task.WhenAll(task1, task2);
}
Which way should I prefer? Are they both equally efficient? Is there a better way of composing this?
I think a better way of composing this would be to create separate async methods for the follow-up Tasks. Something like:
private static async Task Task1(Task task)
{
await task;
Console.WriteLine("Task1, thread: {0}", Thread.CurrentThread.ManagedThreadId);
}
private static async Task Task2(Task task)
{
await task;
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
}
private static async Task Test1()
{
var task = Task.Delay(1000);
var task1 = Task1(task);
var task2 = Task2(task);
await Task.WhenAll(task1, task2);
}
This will act differently than your code with regard to synchronization context (if one is present), but you can affect that by using await task.ConfigureAwait(false).
This is really not an answer, just another alternative
var task = Task.Delay(1000);
await task.ContinueWith(_ =>
{
Parallel.Invoke(
() => {
Console.WriteLine("Task1, thread: {0}",Thread.CurrentThread.ManagedThreadId);
},
() => {
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
});