C# Many longrunning tasks in parallel - c#

I would like to scrape data from one site, so because rapidity is important for my project i must run tasks in parallel. I have a method like this:
public void UpdateData(List<string> myList)
{
while(true)
{
...
...
}
}
And i would like to call the method with different arguments from buttonClick Event so i used this code:
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
var task1 = Task.Factory.StartNew(() => UpdateData(myList1), CancellationToken.None, TaskCreationOptions.LongRunning, uiContext);
var task2 = Task.Factory.StartNew(() => UpdateData(myList2), CancellationToken.None, TaskCreationOptions.LongRunning, uiContext);
The result is after first calling of tasks only the first one continues to update the argument(myList1).
Where is the problem?

You're explicitly asking the Task Scheduler to run tasks on UI context. There is only one UI context, so only one thread will run at a time.
Perform your tasks on non-UI context
When you need the UI context, marshal the calls as needed

Related

How to run multiple methods in parallel in ASP.NET

I have 8 methods in an ASP.NET Console App, like Fun1(), Fun2(), Fun3() ... and so on. In my console application I have called all these methods sequentially. But now the requirement is I have do that using parallel programming concepts. I have read about task and threading concepts in Java but completely new to .NET parallel Programming.
Here is the flow of methods I needed in my console app,
As you can see the diagram, Task1 and Task2 should run in parallel, and Task3 will only occur after completion of the previous two.
The functions inside each task, for example Fun3 and Fun4 for the Task1, should run sequentially, the one after the other.
Can anyone please help me out?
One way to solve this is, by using WhenAll.
To take an example, I have created X number of methods with the name FuncX() like this:
async static Task<int> FuncX()
{
await Task.Delay(500);
var result = await Task.FromResult(1);
return result;
}
In this case, we have Func1, Func3, Func4, Func5, and Func6.
So we call methods and pass them to a list of Task.
var task1 = new List<Task<int>>();
task1.Add(Func3());
task1.Add(Func4());
var task2 = new List<Task<int>>();
task2.Add(Func1());
task2.Add(Func5());
task2.Add(Func6());
You have 2 options to get the result:
// option1
var eachFunctionIsDoneWithAwait1 = await Task.WhenAll(task1);
var eachFunctionIsDoneWithAwait2 = await Task.WhenAll(task2);
var sum1 = eachFunctionIsDoneWithAwait1.Sum() + eachFunctionIsDoneWithAwait2.Sum();
Console.WriteLine(sum1);
// option2
var task3 = new List<List<Task<int>>>();
task3.Add(task1);
task3.Add(task2);
var sum2 = 0;
task3.ForEach(async x =>
{
var r = await Task.WhenAll(x);
sum2 += r.Sum();
});
Console.WriteLine(sum2);
This is just example for inspiration, you can change it and do it the way you want.
Here is how you could create the tasks according to the diagram, using the Task.Run method:
Task task1 = Task.Run(() =>
{
Fun3();
Fun4();
});
Task task2 = Task.Run(() =>
{
Fun1();
Fun5();
Fun6();
});
Task task3 = Task.Run(async () =>
{
await Task.WhenAll(task1, task2);
Fun7();
Fun8();
});
The Task.Run invokes the delegate on the ThreadPool, not on a dedicated thread. If you have some reason to create a dedicated thread for each task, you could use the advanced Task.Factory.StartNew method with the TaskCreationOptions.LongRunning argument, as shown here.
It should be noted that the above implementation has not an optimal behavior in case of exceptions. In case the Fun3() fails immediately, the optimal behavior would be to stop the execution of the task2 as soon as possible. Instead this implementation will execute all three functions Fun1, Fun5 and Fun6 before propagating the error. You could fix this minor flaw by creating a CancellationTokenSource and invoking the Token.ThrowIfCancellationRequested after each function, but it's going to be messy.
Another issue is that in case both task1 and task2 fail, only the exception of the task1 is going to be propagated through the task3. Solving this issue is not trivial.
Update: Here is one way to solve the issue of partial exception propagation:
Task task3 = Task.WhenAll(task1, task2).ContinueWith(t =>
{
if (t.IsFaulted)
{
TaskCompletionSource tcs = new();
tcs.SetException(t.Exception.InnerExceptions);
return tcs.Task;
}
if (t.IsCanceled)
{
TaskCompletionSource tcs = new();
tcs.SetCanceled(new TaskCanceledException(t).CancellationToken);
return tcs.Task;
}
Debug.Assert(t.IsCompletedSuccessfully);
Fun7();
Fun8();
return Task.CompletedTask;
}, default, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default)
.Unwrap();
In case both task1 and task2 fail, the task3 will propagate the exceptions of both tasks.

Tasks freezing the interface

I am using 3 Tasks to execute 3 tasks simultaneously, however, when started all the tasks there is no freezing of the GUI, it only gets a bit slow ... when it returns the result of the last task it totally freezes and stops updating the GUI ...
async Task UpdateBlockChain()
{
var task = Task.Factory.StartNew((Action) =>
{
while (true)
{
BlockChain blockChain = new BlockChain();
coinList[0].Price = blockChain.GetDataByNode("last");
coinList[0].Low = blockChain.GetDataByNode("low");
coinList[0].High = blockChain.GetDataByNode("high");
RefreshView();
Task.Delay(1000);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
await Task.Delay(500);
}
async Task UpdateBitFinex()
{
var task = Task.Factory.StartNew((Action) =>
{
while (true)
{
Bitfinex bitFinex = new Bitfinex();
coinList[1].Price = bitFinex.GetDataByNode("last_price");
coinList[1].Low = bitFinex.GetDataByNode("low");
coinList[1].High = bitFinex.GetDataByNode("high");
RefreshView();
Task.Delay(2000);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
await Task.Delay(500);
}
async Task UpdateBitstamp()
{
var task = Task.Factory.StartNew((Action) =>
{
while (true)
{
Bitstamp bitstamp = new Bitstamp();
coinList[2].Price = bitstamp.GetDataByNode("last");
coinList[2].Low = bitstamp.GetDataByNode("low");
coinList[2].High = bitstamp.GetDataByNode("high");
RefreshView();
Task.Delay(1000);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
await Task.Delay(500);
}
Refresh View:
void RefreshView()
{
if (dataGridView1.InvokeRequired)
{
dataGridView1.BeginInvoke(new Action(() =>
{
dataGridView1.Update();
dataGridView1.Refresh();
}));
}
}
Run task:
await UpdateBlockChain();
await UpdateBitFinex();
await UpdateBitstamp();
Here is a example of class https://pastebin.com/DuQybhcz
I do not know the methods I am using are wrong, I apologize for code flow error.
This is because you use TaskScheduler.FromCurrentSynchronizationContext which is supposed to schedule the tasks to run on the same thread as the calling one, which in your case is UI thread.
Offload all your work to background threads, and only marshal the ui refresh operations to your UI thread by using BeginInvoke
Also you absolutely have to call await Task.Delay(xxx) inside of your while loops, otherwise there are no delays between cycles, and they are very CPU intensive, and schedule too many UI updates
To quote MSDN on TaskScheduler
You can use the TaskScheduler.FromCurrentSynchronizationContext method
to specify that a task should be scheduled to run on a particular
thread. This is useful in frameworks such as Windows Forms and Windows
Presentation Foundation where access to user interface objects is
often restricted to code that is running on the same thread on which
the UI object was created. For more information, see How to: Schedule
Work on the User Interface (UI) Thread.
The following example uses the
TaskScheduler.FromCurrentSynchronizationContext method in a Windows
Presentation Foundation (WPF) app to schedule a task on the same
thread that the user interface (UI) control was created on.

Running method in the background and UI Thread WPF

I'm having troubles with the following example:
public void Method()
{
LongRunningMethod();
}
LongRunningMethod() takes around 5 seconds to invoke. I am invoking Method() from the UI thread, so it obviously should freeze the UI. The solution for that is to run Method() within a new Task so I am running it like this:
Task.Factory.StartNew(()=>{Method()})
It's still blocking the UI so I thought whether LongRunningMethod() is using the UI context probably. Then I tried another solution:
new Thread(()=>Method()).Start()
and it started working. How is that possible? I know that Task is not guaranteed to be run on a different thread but CLR should be smart enough to figure out that it's long running method.
You are scheduling work on the User Interface (UI) Thread cause you are using
TaskScheduler.FromCurrentSynchronizationContext()) in this code:
Task nextTask = task.ContinueWith(x =>
{
DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
}
And this is a reason why your UI is frozen. To prevent try to change TaskScheduler to Default:
Task task = Task.Run(() => {; });
Task nextTask = task.ContinueWith(x =>
{
//DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
Task.Factory.StartNew is dangerous cause it uses TaskScheduler.Current as opposed to TaskScheduler.Default. To prevent this use Task.Run which always points to TaskScheduler.Default. Task.Run is new in .NET 4.5, if you're in .NET 4.0 you can create your TaskFactory with default parameters.
As MSDN says:
TaskScheduler.FromCurrentSynchronizationContext()) means schedule
a task on the same thread that the user interface (UI) control was
created on.
Update:
What happens when you run method RunTask():
var task = new Task(action, cancellationTokenSource.Token);
create a "task". (task is not run. The "task" is just queed to the ThreadPool.)
Task nextTask = task.ContinueWith(x =>
{
DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
create a "nextTask" which will start performing AFTER "task" is completed and the "nextTask" will be performed on UI thread as you've set a feature
TaskScheduler.FromCurrentSynchronizationContext().
task.Start();
You run your "task". When the "task" is completed, then "nextTask" is run by method "task.ContinuuWith()" which will be performed on UI thread you wrote (TaskScheduler.FromCurrentSynchronizationContext()
So to sum up, the two your tasks are interconnected and continuation of task is performed on UI thread which is a reason to freeze your UI. To prevent this behavior use TaskScheduler.Default.
This is exactly how it looks like:
public void RunTask(Action action){
var task = new Task(action, cancellationTokenSource.Token);
Task nextTask = task.ContinueWith(x =>
{
DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
}
public void DoSomething()
{
if(condition) // condition is true in this case (it's recurency but not permanent)
RunTask(() => Method()); // method is being passed which blocks UI when invoked in RunTask method
}
public void Method()
{
LongRunningMethod();
}
This is the starting point invocation (UI Thread):
RunTask(()=>Action());
Only a guess: Thread.Start creates a foreground thread. Maybe the method switches to a known foreground-thread when it detects, that it is run from a background-thread.
Hope it helps somehow.

Parallel.Invoke does not wait for async methods to complete

I have an application that pulls a fair amount of data from different sources. A local database, a networked database, and a web query. Any of these can take a few seconds to complete. So, first I decided to run these in parallel:
Parallel.Invoke(
() => dataX = loadX(),
() => dataY = loadY(),
() => dataZ = loadZ()
);
As expected, all three execute in parallel, but execution on the whole block doesn't come back until the last one is done.
Next, I decided to add a spinner or "busy indicator" to the application. I don't want to block the UI thread or the spinner won't spin. So these need to be ran in async mode. But if I run all three in an async mode, then they in affect happen "synchronously", just not in the same thread as the UI. I still want them to run in parallel.
spinner.IsBusy = true;
Parallel.Invoke(
async () => dataX = await Task.Run(() => { return loadX(); }),
async () => dataY = await Task.Run(() => { return loadY(); }),
async () => dataZ = await Task.Run(() => { return loadZ(); })
);
spinner.isBusy = false;
Now, the Parallel.Invoke does not wait for the methods to finish and the spinner is instantly off. Worse, dataX/Y/Z are null and exceptions occur later.
What's the proper way here? Should I use a BackgroundWorker instead? I was hoping to make use of the .NET 4.5 features.
It sounds like you really want something like:
spinner.IsBusy = true;
try
{
Task t1 = Task.Run(() => dataX = loadX());
Task t2 = Task.Run(() => dataY = loadY());
Task t3 = Task.Run(() => dataZ = loadZ());
await Task.WhenAll(t1, t2, t3);
}
finally
{
spinner.IsBusy = false;
}
That way you're asynchronously waiting for all the tasks to complete (Task.WhenAll returns a task which completes when all the other tasks complete), without blocking the UI thread... whereas Parallel.Invoke (and Parallel.ForEach etc) are blocking calls, and shouldn't be used in the UI thread.
(The reason that Parallel.Invoke wasn't blocking with your async lambdas is that it was just waiting until each Action returned... which was basically when it hit the start of the await. Normally you'd want to assign an async lambda to Func<Task> or similar, in the same way that you don't want to write async void methods usually.)
As you stated in your question, two of your methods query a database (one via sql, the other via azure) and the third triggers a POST request to a web service. All three of those methods are doing I/O bound work.
What happeneds when you invoke Parallel.Invoke is you basically trigger three ThreadPool threads to block and wait for I/O based operations to complete, which is pretty much a waste of resources, and will scale pretty badly if you ever need to.
Instead, you could use async apis which all three of them expose:
SQL Server via Entity Framework 6 or ADO.NET
Azure has async api's
Web request via HttpClient.PostAsync
Lets assume the following methods:
LoadXAsync();
LoadYAsync();
LoadZAsync();
You can call them like this:
spinner.IsBusy = true;
try
{
Task t1 = LoadXAsync();
Task t2 = LoadYAsync();
Task t3 = LoadZAsync();
await Task.WhenAll(t1, t2, t3);
}
finally
{
spinner.IsBusy = false;
}
This will have the same desired outcome. It wont freeze your UI, and it would let you save valuable resources.

How to determine when all task is completed

here is sample code for starting multiple task
Task.Factory.StartNew(() =>
{
//foreach (KeyValuePair<string, string> entry in dicList)
Parallel.ForEach(dicList,
entry =>
{
//create and add the Progress in UI thread
var ucProgress = (Progress)fpPanel.Invoke(createProgress, entry);
//execute ucProgress.Process(); in non-UI thread in parallel.
//the .Process(); must update UI by using *Invoke
ucProgress.Process();
System.Threading.Thread.SpinWait(5000000);
});
});
.ContinueWith(task =>
{
//to handle exceptions use task.Exception member
var progressBar = (ProgressBar)task.AsyncState;
if (!task.IsCancelled)
{
//hide progress bar here and reset pb.Value = 0
}
},
TaskScheduler.FromCurrentSynchronizationContext() //update UI from UI thread
);
when we start multiple task using Task.Factory.StartNew() then we can use .ContinueWith() block to determine when each task finish. i mean ContinueWith block fire once for each task completion. so i just want to know is there any mechanism in TPL library. if i start 10 task using Task.Factory.StartNew() so how do i notify after when 10 task will be finish. please give some insight with sample code.
if i start 10 task using Task.Factory.StartNew() so how do i notify after when 10 task will be finish
Three options:
The blocking Task.WaitAll call, which only returns when all the given tasks have completed
The async Task.WhenAll call, which returns a task which completes when all the given tasks have completed. (Introduced in .NET 4.5.)
TaskFactory.ContinueWhenAll, which adds a continuation task which will run when all the given tasks have completed.
if i start 10 task using Task.Factory.StartNew() so how do i notify after when 10 task will be finish
You can use Task.WaitAll. This call will block current thread until all tasks are finished.
Side note: you seem to be using Task, Parallel and Thread.SpinWait, which makes your code complex. I would spend a bit of time analysing if that complexity is really necessary.
You can use the WaitAll(). Example :
Func<bool> DummyMethod = () =>{
// When ready, send back complete!
return true;
};
// Create list of tasks
System.Threading.Tasks.Task<bool>[] tasks = new System.Threading.Tasks.Task<bool>[2];
// First task
var firstTask = System.Threading.Tasks.Task.Factory.StartNew(() => DummyMethod(), TaskCreationOptions.LongRunning);
tasks[0] = firstTask;
// Second task
var secondTask = System.Threading.Tasks.Task.Factory.StartNew(() => DummyMethod(), TaskCreationOptions.LongRunning);
tasks[1] = secondTask;
// Launch all
System.Threading.Tasks.Task.WaitAll(tasks);
Another solution:
After the completion of all the operation inside Parallel.For(...) it return an onject of ParallelLoopResult, Documentation:
For returns a System.Threading.Tasks.ParallelLoopResult object when
all threads have completed. This return value is useful when you are
stopping or breaking loop iteration manually, because the
ParallelLoopResult stores information such as the last iteration that
ran to completion. If one or more exceptions occur on one of the
threads, a System.AggregateException will be thrown.
The ParallelLoopResult class has a IsCompleted property that is set to false when a Stop() of Break() method has been executed.
Example:
ParallelLoopResult result = Parallel.For(...);
if (result.IsCompleted)
{
//Start another task
}
Note that it advised to use it only when breaking or stoping the loop manually (otherwise just use WaitAll, WhenAll etc).

Categories

Resources