Why to use Async inside Task.Run? - c#

While working on some code, I came across the following,
Approach 1:
private void MyMethod()
{
var t = Task.Run(
async () =>
{
Foo.Fim();
await Task.Delay(5000);
});
t.Wait();
}
We deliberately introduce a delay of 5 seconds to allow Fim() to complete its work. It is required to introduce the delay - imagine this to be calling a third party API which mandates a cooling period of 5 seconds.
I wanted to understand if this the right approach to wait for an operation to complete. How is Approach 1 different from the following Approach 2? Or is there a better way to do this? All we need is to have the cooling period delay and avoid blocking the UI.
Approach 2:
private void MyMethod()
{
var t = Task.Run(
() =>
{
Foo.Fim();
Task.Delay(5000).Wait(); // some operation which takes 5 seconds.
});
t.Wait();
}

What you need is a throttler, and a handy class for implementing one is the SemaphoreSlim:
Limits the number of threads that can access a resource or pool of resources concurrently.
private SemaphoreSlim _myMethodSemaphore = new SemaphoreSlim(1);
private void MyMethod()
{
_ = Task.Run(async () =>
{
await _myMethodSemaphore.WaitAsync();
try
{
Foo.Fim();
}
finally
{
await Task.Delay(5000); // Impose the delay even in case of an exception
_myMethodSemaphore.Release();
}
});
}
Note that there is no Wait() in this example. The only blocking call is the Foo.Fim() method call. By making this method asynchronous you could have a perfect solution regarding scalability: await Foo.FimAsync()

Related

SemaphoreSlim problems with async tasks

I've been attempting to use SemaphoreSlim to limit the amount of concurrent tasks I have running at any one time but it seems to have no effect, likely down to my implementation which is why I'm here. My SemaphoreSlim code is like so:
First it's called by
await Task.Run(() => mc.StartAsync());
Calling this method
public async Task StartAsync()
{
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(5))
{
foreach (var task in ThreadHandler.ThreadList)
{
await concurrencySemaphore.WaitAsync();
try
{
await Task.Run(() => task.Operation.Start());
}
finally
{
concurrencySemaphore.Release();
}
}
}
}
This in turn is starting a task from a list which looks like this, this is in a custom model with the task stored and created previously
public Task Operation { get; set; }
var t = new Task(async () =>
{
await Task.Run(() => Method(input, sps));
});
Remembering my code doesn't work as I'd expect, is this the correct way to start something like this? I don't expect 3 tasks launching from a single point is a good idea. The main reason It's like this is because I'm executing an Action<> and couldn't figure out a way to await it alone. Do these tasks count towards the SemaphoreSlim limit?
After various tests I can confirm that my SemaphoreSlim code is just continuously executing the tasks, I added a large task delay into the list of tasks to see if I could stop it from executing which worked but new tasks were still launched... what am I missing?
My goal is to have a limit on the number of tasks concurrently running, if that wasn't clear. Thank you for any assistance!
EDIT: I think I've realised I'm only awaiting the starting of the task, not the completion.
I think I've realised I'm only awaiting the starting of the task, not the completion.
Indeed, that is the core of the problem.
You shouldn't use the Task constructor, ever, at all, for anything. Just pretend it doesn't exist. It will always lead you down an awkward path.
If you have an action you want to perform at a later time, you should use a delegate: Action or Func<T> for synchronous work, and Func<Task> or Func<Task<T>> for asynchronous work. E.g., if Method is synchronous, then you would have:
public Action Operation { get; set; }
...
Operation = () => Method(input, sps);
Then you can invoke it using Task.Run as such:
public async Task ProcessAsync()
{
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(5))
{
var tasks = ThreadHandler.ThreadList.Select(async task =>
{
await concurrencySemaphore.WaitAsync();
try
{
await Task.Run(() => task.Operation());
}
finally
{
concurrencySemaphore.Release();
}
}).ToList();
await Task.WhenAll(tasks);
}
}
The above code will work fine if Operation is Action (synchronous) or Func<Task> (asynchronous).
However, if it is Action (i.e., synchronous), then what you're really doing is parallel processing, not asynchronous concurrency, and there's built-in types that can help with that:
public void Process()
{
// Only valid if Operation is Action, not Func<Task>!
Parallel.ForEach(
ThreadHandler.ThreadList,
new ParallelOptions { MaxDegreeOfParallelism = 5 },
task => task.Operation());
}

Running two tasks with Parallel.Invoke and add a timeout in case one task takes longer

I'm calling two functions that rely on some external web services. For now, they run in parallel and when they both complete the execution resumes. However, if the external servers take too much time to process the requests, it could lock my code for a while.
I want to add a timeout so that if the servers take more than 10 seconds to respond then just continue on. This is what I have, how can I add a timeout?
Parallel.Invoke(
() => FunctionThatCallsServer1(TheParameter),
() => FunctionThatCallsServer2(TheParameter)
);
RunThisFunctionNoMatterWhatAfter10Seconds();
I don't think there's an easy way of timing out a Parallel.Invoke once the functions have started, which clearly they will have done after ten seconds here. Parallel.Invoke waits for the functions to complete even if you cancel, so you would have to find a way to complete the functions early.
However, under the covers Parallel.Invoke uses Tasks, and if you use Tasks directly instead of Parallel.Invoke then you can provide a timeout. The code below shows how:
Task task1 = Task.Run(() => FunctionThatCallsServer1(TheParameter));
Task task2 = Task.Run(() => FunctionThatCallsServer2(TheParameter));
// 10000 is timeout in ms, allTasksCompleted is true if they completed, false if timed out
bool allTasksCompleted = Task.WaitAll(new[] { task1, task2 }, 10000);
RunThisFunctionNoMatterWhatAfter10Seconds();
One slight difference this code has with Parallel.Invoke is that if you have a VERY large number of functions then Parallel.Invoke will manage the Task creation better than just blindly creating a Task for every function as here. Parallel.Invoke will create a limited number of Tasks and re-use them as the functions complete. This won't be an issue with just a few functions to call as above.
You will need to create an instance of CancellationTokenSource and right at creating time you ca configure your timeout time, like
var cts = new CancellationTokenSource(timeout);
then you will need to create an instance of ParallelOptions where you set the ParallelOptions.CancellationToken to the token of the CancellationTokenSource, like
var options = new ParallelOptions {
CancellationToken = cts.Token,
};
Then you can call Parallel.Invoke with the options and your actions
try
{
Parallel.Invoke(
options,
() => FunctionThatCallsServer1(token),
() => FunctionThatCallsServer2(token)
);
}
catch (OperationCanceledException ex)
{
// timeout reached
Console.WriteLine("Timeout");
throw;
}
but you will also need to hand the token to the called Server functions and handle the timeout in these actions aswell.
This is because the Parallel.Invoke will only check before it starts an action if the token it got is cancelled. That means if all actions are started before the timeout occures the Parallel.Invoke call will block as long as the actions need to finish.
Update:
A good way to test the cancellation is to define FunctionThatCallsServer1 like,
static void FunctionThatCallsServer1(CancellationToken token) {
var endTime = DateTime.Now.AddSeconds(5);
while (DateTime.Now < endTime) {
token.ThrowIfCancellationRequested();
Thread.Sleep(1);
}
}
Below is the code:
using System;
using System.Threading.Tasks;
namespace Algorithums
{
public class Program
{
public static void Main(string[] args)
{
ParelleTasks();
Console.WriteLine("Main");
Console.ReadLine();
}
private static void ParelleTasks()
{
Task t = Task.Run(() => {
FunctionThatCallsServers();
Console.WriteLine("Task ended after 20 Seconds");
});
try
{
Console.WriteLine("About to wait for 10 sec completion of task {0}", t.Id);
bool result = t.Wait(10000);
Console.WriteLine("Wait completed normally: {0}", result);
Console.WriteLine("The task status: {0:G}", t.Status);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Error: " + e.ToString());
}
RunThisFunctionNoMatterWhatAfter10Seconds();
}
private static bool FunctionThatCallsServers()
{
Parallel.Invoke(
() => FunctionThatCallsServer1(),
() => FunctionThatCallsServer2()
);
return true;
}
private static void FunctionThatCallsServer1()
{
System.Threading.Thread.Sleep(20000);
Console.WriteLine("FunctionThatCallsServer1");
}
private static void FunctionThatCallsServer2()
{
System.Threading.Thread.Sleep(20000);
Console.WriteLine("FunctionThatCallsServer2");
}
private static void RunThisFunctionNoMatterWhatAfter10Seconds()
{
Console.WriteLine("RunThisFunctionNoMatterWhatAfter10Seconds");
}
}
}

Nested await or async's "glutting"

Let's consider the next procedures hierarhy
Main.cs:
// timer callback
{
Plot.UpdateData();
}
Plot.cs:
public async void UpdateData()
{
await CalculateData();
// ...
}
private async Task CalculateData()
{
await Calculations.Calculate();
// UI updating
// ...
}
Calculations.cs:
public static async Task<bool> Calculate()
{
async Task<bool> CalculateLR()
{
var task1 = Calculate1();
var task2 = Calculate2();
await Task.WhenAll(new[] { task1, task2 });
return true;
}
var leftTask = CalculateLR();
var rightTask = CalculateLR();
await Task.WhenAll(new[] { leftTask, rightTask });
await Calculate3();
return true;
}
Here I have some basic calculations (in Calculate1-Calculate3 procedures) of Calculations.cs file and some interaction with UI. The "entry point" Plot.UpdateData is placed in Device.StartTimer( block of the main form.
It works, but I think this structure creates excess threads. So my question is can this hierarhy be optimized without loss of asynchronous advantages?
Or, other words, which procedures should be awaited in case of nested calls. Where is first non-awaited call should lie? Thanks.
First thing to note: async/await is about tasks, not threads. Under certain circumstances, a task can be thread equivalent, and in most cases it is not (the same thread can serve a lot of tasks sequentially conveyor-style, depending on how they're scheduled for continuation and what is awaiting condition, for example).
And I could strongly recommend to address this for further reading as very comprehensive source:
https://blog.stephencleary.com/2013/11/there-is-no-thread.html
https://blog.stephencleary.com/2014/05/a-tour-of-task-part-1-constructors.html

Run two task asynchronously

I'm running a synchronous method. Inside it I have to run two big method, so I was thinking to run them asynchronously.. I was thinking something like
public void MyFunc()
{
var doWorkTask_1 = DoWork1();
var doWorkTask_2 = DoWork2();
var result1 = await doWorkTask_1;
var result2 = await doWorkTask_2;
if(result1 == result2)
....
Thread.Sleep(syncInterval);
}
To do this i need that:
DoWork1 and DoWork2 are asynchronous;
MyFunc is asynchrous too;
But no method is asynchronous!!!
SO I tried to do in another way:
public void MyFunc()
{
var doWorkTask_1 = Task.Run(() => DoWork1());
var doWorkTask_2 = Task.Run(() => DoWork2());
var result1 = doWorkTask_1.Result;
var result2 = doWorkTask_2.Result;
if(result1 == result2)
....
Thread.Sleep(syncInterval);
}
So, 1st question:
Do I have written same thing in two different ways?
2nd question. I have to run the MyFunc method every X time, so I call it in this way:
Task.Factory.StartNew(MyFunc);
Can I call it simply
MyFunc();
My question is because inside myFunc I have a Thread.Sleep. Can I let sleep the main thread or is better to let sleep a thread inside the main?
I hope I have been clear.
Thank you.
Have I written same thing in two different ways?
No. Your first method will execute two units of work in parallel, and will asynchronously wait on the first one, then the second one.
Your second method will execute two units of work in parallel, and will synchronously wait on the first one, then on the second one.
Can I let sleep the main thread or is better to let sleep a thread
inside the main?
That depends on what your application is doing. You could turn MyFunc to be async so you can use Task.Delay instead, which internally uses a timer and doesn't block (and you may also pass it a CancellationToken if needed):
public async Task MyFuncAsync()
{
// Do work
await Task.Delay(syncInterval);
}
Side note:
It seems to me like you may be using async over sync, which in general is a questionable approach. I would advise against it.
Instead, like in your first example, explicitly invoke Task.Run on these workers:
public async Task MyFuncAsync()
{
var firstTask = Task.Run(() => DoWork1());
var secondTask = Task.Run(() => DoWork2());
await Task.WhenAll(new[] { firstTask, secondTask });
await Task.Delay(syncInterval);
}
Use Task.WhenAll to create a new task, encapsulating both your worker tasks.
Creates a task that will complete when all of the supplied tasks have
completed.
https://msdn.microsoft.com/en-us/library/hh194874%28v=vs.110%29.aspx
public async void MyFunc()
{
var doWorkTask_1 = DoWork1();
var doWorkTask_2 = DoWork2();
var results = await Task.WhenAll(doWorkTask_1, doWorkTask_2);
}
If you can't do async all the way, and by asynchronous you mean that you want to process DoWork1 and DoWork2 concurrently on different threads then you can use Task.Run to offload the work to a different thread and Task.WaitAll to wait synchronously for both tasks to complete:
public void MyFunc()
{
var task1 = Task.Run(() => DoWork1());
var task2 = Task.Run(() => DoWork2());
Task.WaitAll(task1, task2);
if (task1.Result == task2.Result)
{
// ...
}
Thread.Sleep(syncInterval);
}
Now, since this uses 3 threads (two ThreadPool threads in Task.Run and the calling thread blocked on Task.WaitAll) when we only need 2 we can simplify and optimize the example by executing one of the operations on the calling thread:
public void MyFunc()
{
var task1 = Task.Run(() => DoWork1());
var result2 = DoWork2();
if (task1.Result == result2)
{
// ...
}
Thread.Sleep(syncInterval);
}

Right way of using TPL for this specific constellation ( Continuous parallel Tasks )

i hope i can get some help here, tried different things that work, but always with some problems. What would be the best way to achieve with the sample code the following:
Task A + B + C should run parallel, but Task B should start with a small delay to task A.
Depending on the result of the single Tasks devsearch/conssearch, they should just restart themselves or start task "GO". GO will run only once and will restart the calling task.
As already said, what would be the best way to do this. Is Parallel.Invoke here the suitable option? Should i switch to Tasks and move it to the Main Section?
I really appreciate any help. Thanks in Advance. Please do not care about small faults in the tasks or the details, just built this as example to do not have it too complex.
UPDATE/Additional Information:
I have a Windows Form Application. The logic is the following:
I have a start button, which starts "public async void button1_Click"
I have 3 Tasks
"private async Task conssearch"
"private async Task devsearch"
"private async Task GO"
For the tasks conssearch and devsearch i give the parameters who is the sender, a bool, an Account and an Array.
In the tasks i perform asynch http requests with await. This request is done for each item in the Array. For the http requests i need logindata, this is passed via the Account.
I can login multiple times ( Cookiehandling etc. is done and working ), that is why i want to run conssearch parallel at the same time. The delay between the parallel execution of conssearch is needed,
bcause i don´t want to have the same results.
I have a rps limit per account, that is why i have await.delay within the single tasks.
So in summary i want to start parallel conssearch x 2 and devsearch when i press the Start Button.
Now Every single task should run independant from each other.
If i get in the Tasks conssearch and devsearch the result i am expecting, i will start "private async Task GO" and pass the same parameters. This is done bcause i want that "private async Task GO" starts
the calling Task again after it is finished.
If i do not get the expected result, the Task should restart
I hope it is now a bit better understandable what i am trying to do.
public async void button1_Click(object sender, EventArgs e)
{
while (true)
{
Parallel.Invoke(
() => {
Task taskA = Task.Factory.StartNew(() => conssearch("cons", false, Account1, devArr));
System.Threading.Thread.Sleep(335);
Task taskB = Task.Factory.StartNew(() => conssearch("cons", false, Account2, devArr));
taskA.Wait();
taskB.Wait();
},
() => Task.Run(() => devsearch("dev", false, Account3, devArr)),
);
}
}
private async Task conssearch(object sender, bool GO, string acc, Array[] devArr)
{
for (int i = 0; i < devArr.Length; i++)
{
// Do some HTTP here....
If ....
GO = True;
}
await Task.Delay(100);
if (GO)
{
listView1.BeginInvoke(new MethodInvoker(() => listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)));
listView2.BeginInvoke(new MethodInvoker(() => listView2.Items.Clear()));
GO("cons", acc, devArr);
}
else
{
//conssearch("cons", false, acc, devArr)
}
}
private async Task devsearch(object sender, bool GO, string acc, Array[] devArr)
{
for (int i = 0; i < devArr.Length; i++)
{
// Do some HTTP here....
If ....
GO = True;
}
await Task.Delay(100);
if (GO)
{
listView1.BeginInvoke(new MethodInvoker(() => listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)));
listView2.BeginInvoke(new MethodInvoker(() => listView2.Items.Clear()));
GO("cons", acc, devArr);
}
else
{
//devsearch("dev", false, acc, devArr)
}
}
private async Task GO(object sender, string acc, Array[] devArr)
{
{
// Do some HTTP here....
}
await Task.Delay(100);
if (sender == "tra")
await conssearch(sender, false, client, devArr);
else
await devsearch(sender, false, client, devArr);
}
I'm going to focus only on running the three tasks, nothing else.
First of all, starting one task, waiting a constant amount of time and then starting a second task sounds suspicious. What is the purpose of the delay? I can't think of anything that would make the most sense to represent this way.
Now, combining Parallel and Task usually doesn't make much sense. If one of the Parallel methods works for you, use that, not Tasks. If you need something more complicated, use Tasks.
In your case, since you can use await, I would take advantage of that: have one async method that starts the conssearch Tasks with the delay. Then, in another method, call that method, start the devsearch Task and wait for both to complete. Something like:
async Task RunConsSearches()
{
var taskA = Task.Run(() => ConsSearch("cons", false, Account1, devArr));
await Task.Delay(335);
var taskB = Task.Run(() => ConsSearch("cons", false, Account2, devArr));
await Task.WhenAll(taskA, taskB);
}
…
var consSeachTask = RunConsSearches();
var devSearchTask = Task.Run(() => DevSearch("dev", false, Account3, devArr));
await Task.WhenAll(consSeachTask, devSearchTask);
Even better approach would be to convert ConsSearch and DevSearch to proper async, but it's difficult to tell how hard would that be.

Categories