Awaiting task array which is executed in lock - c#

TestAwaitTaskArrayAsync() can be called from several places in code. I need to lock execution of taskArray and wait asynchronously till its all tasks are finished before next call will start executing taskArray. Here is the code:
private async Task TestAwaitTaskArrayAsync()
{
Task[] taskArray;
lock (_lock_taskArray)
{
taskArray = new Task[]
{
Task.Run(() =>
{
SomeMethod1();
}),
Task.Run(() =>
{
SomeMethod2();
})
};
}
await Task.WhenAll(taskArray);
}
Await in lock is not allowed so I could use AsyncLock if necessary, but trying keep it simple. Is this code correct and thread safe? I am not sure if await Task.WhenAll(taskArray); can be outside of lock, should I use AsyncLock instead?

The lock you're using has almost no effect because creating the tasks is very fast and does not conflict with anything. The way you achieve mutual exclusion in an async setting is with the SemaphoreSlim class. It is a lock that supports the Task-async pattern.
SemaphoreSlim sem = new SemaphoreSlim(1);
private async Task TestAwaitTaskArrayAsync()
{
await sem.WaitAsync();
try {
Task[] taskArray = new Task[]
{
Task.Run(() =>
{
SomeMethod1();
}),
Task.Run(() =>
{
SomeMethod2();
})
};
}
await Task.WhenAll(taskArray);
}
finally { sem.Release(); }
}
In a synchronous way this would have been easier:
lock (_lock_taskArray)
Parallel.Invoke(() => SomeMethod1(), () => SomeMethod2());
Done.
You can also use AsyncLock if you like. That should allow you to use the using construct to release the lock reliably.

Related

Task.Continue with task.IsFaulted not capturing exceptions [duplicate]

I'm trying to use Task.WaitAll on a list of tasks. The thing is the tasks are an async lambda which breaks Tasks.WaitAll as it never waits.
Here is an example code block:
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
using (dbContext = new DatabaseContext())
{
var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
//do long cpu process here...
}
}
Task.WaitAll(tasks);
//do more stuff here
This doesn't wait because of the async lambda. So how am I supposed to await I/O operations in my lambda?
Task.Factory.StartNew doesn't recognise async delegates as there is no overload that accepts a function returning a Task.
This plus other reasons (see StartNew is dangerous) is why you should be using Task.Run here:
tasks.Add(Task.Run(async () => ...
This doesn't wait because of the async lambda. So how am I supposed to
await I/O operations in my lambda?
The reason Task.WaitAll doesn't wait for the completion of the IO work presented by your async lambda is because Task.Factory.StartNew actually returns a Task<Task>. Since your list is a List<Task> (and Task<T> derives from Task), you wait on the outer task started by StartNew, while ignoring the inner one created by the async lambda. This is why they say Task.Factory.StartNew is dangerous with respect to async.
How could you fix this? You could explicitly call Task<Task>.Unwrap() in order to get the inner task:
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
using (dbContext = new DatabaseContext())
{
var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
//do long cpu process here...
}
}).Unwrap());
Or like others said, you could call Task.Run instead:
tasks.Add(Task.Run(async () => /* lambda */);
Also, since you want to be doing things right, you'll want to use Task.WhenAll, why is asynchronously waitable, instead of Task.WaitAll which synchronously blocks:
await Task.WhenAll(tasks);
You can do like this.
void Something()
{
List<Task> tasks = new List<Task>();
tasks.Add(ReadAsync());
Task.WaitAll(tasks.ToArray());
}
async Task ReadAsync() {
using (dbContext = new DatabaseContext())
{
var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
//do long cpu process here...
}
}
you have to use the Task.ContinueWith method. Like this
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(() =>
{
using (dbContext = new DatabaseContext())
{
return dbContext.Where(r => r.Id = 100).ToListAsync().ContinueWith(t =>
{
var records = t.Result;
// do long cpu process here...
});
}
}
}

Asynchronous Parallel threads

I want to run a function asynchronously in parallel for all the objects in a collection.
Here's the current code snippet.
Parallel.ForEach(Objs,
new ParallelOptions { MaxDegreeOfParallelism = 10 },
item =>
{
DoSomething(someParameter, item);
});
How can I achieve this is with Async-Parallel Threads
It's not clear what you mean by "asynchronously in parallel," since asynchrony (using fewer threads) is kind of the opposite of parallel (using more threads).
But if you mean that you want to run the asynchronous method DoSomething concurrently, then that can be done via Task.WhenAll:
var tasks = Objs.Select(item => DoSomething(someParamter, item));
await Task.WhenAll(tasks);
If you want to limit the degree of concurrency (e.g., to 10), then you can use a SemaphoreSlim:
var semaphore = new SemaphoreSlim(10);
var tasks = Objs.Select(item =>
{
await semaphore.WaitAsync();
try { await DoSomething(someParameter, item); }
finally { semaphore.Release(); }
});
await Task.WhenAll(tasks);

Cancellation of multiple tasks in TPL based on return resutl

I have a scenario in which i have multiple functions to call in parallel and I am using TPL for that task. I have used
ConcurrentDictionary<string, List<result>> dictionary = new ConcurrentDictionary<string, List<result>>();
var tasks = Task.Run(()=>
{
new Task(()=> {dictionary.TryAdd("First", CallFirstFunction());});
new Task(()=> {dictionary.TryAdd("Second", CallSecondFunction());});
new Task(()=> {dictionary.TryAdd("Third", CallThirdFunction());});
new Task(()=> {dictionary.TryAdd("Fourth", CallFourthFunction());});
});
Now, i need to wait till all the function are executed and return some result in concurrent dictionary so i can used it for further process, But i also want to cancel any task if some returned result is empty regardless of task order. If any function returns the empty result i need to cancel all remaining tasks at the same time. I have checked the "CancellationToken" class as well but i am confused to use this with in the tasks and how to share the function states with in the tasks.
Any help will be appreciated.
Best Regards
To support cancellation you need to work with CancellationTokenSource. See https://msdn.microsoft.com/en-us/library/mt674886.aspx
What you need to do is to pass the CancellationTokenSource to your methods so when the result is empty you can call CancellationTokenSource.Cancel()
async Task DoWorkAsync()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
var tasks = new List<Task>
{
Task.Run(() => AddToDictionary("First", () => CallFirstFunction(), cts), ct),
Task.Run(() => AddToDictionary("Second", () => CallSecondFunction(), cts), ct),
Task.Run(() => AddToDictionary("Third", () => CallThirdFunction(), cts), ct),
Task.Run(() => AddToDictionary("Fourth", () => CallFourthFunction(), cts), ct),
};
try
{
await Task.WhenAll(tasks);
}
catch (OperationCanceledException ex)
{
}
}
private void AddToDictionary(string key, Func<List<int>> method, CancellationTokenSource cts)
{
if(cts.IsCancellationRequested)
return;
var result = method.Invoke();
if(!result.Any());
cts.Cancel();
if(!cts.IsCancellationRequested)
dictionary.TryAdd(key, result);
}
This example assumes that the operations you call (CallFirstFunction etc.) are not async (don't return a Task or Task). Otherwise you should pass the CancellationToken to them as well and check for cancellation using ct.ThrowIfCancellationRequested
Cancelling a Task generates an OperationCanceledException you need to handle.
Of course, when any method is already completed before another one returns an empty result, those results will already be present in the dictionary. If you need other behaviour this example will give you some basic idea about how the CancellationTokenSource relates to Tasks and CancellationTokens

Multiple worker threads vs One worker with async/await

My code currently has the following 10 worker threads. Each worker thread continues polling a job from the queue and then process the long running job.
for (int k=0; k<10; k++)
{
Task.Factory.StartNew(() => DoPollingThenWork(), TaskCreationOptions.LongRunning);
}
void DoPollingThenWork()
{
while (true)
{
var msg = Poll();
if (msg != null)
{
Thread.Sleep(3000); // process the I/O bound job
}
}
}
I am refactoring the underlying code to use async/await pattern. I think I can rewrite the above code to the followings. It uses one main thread that keeps creating the async task, and use SemaphoreSlim to throttle the number of concurrent tasks to 10.
Task.Factory.StartNew(() => WorkerMainAsync(), TaskCreationOptions.LongRunning);
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
while (true)
{
await ss.WaitAsync();
Task.Run(async () =>
{
await DoPollingThenWorkAsync();
ss.Release();
});
}
}
async Task DoPollingThenWorkAsync()
{
var msg = Poll();
if (msg != null)
{
await Task.Delay(3000); // process the I/O-bound job
}
}
Both should behave the same. But I think the second options seems better because it doesn't block the thread. But the downside is I can't do Wait (to gracefully stop the task) since the task is like fire and forget. Is the second option the right way to replace the traditional worker threads pattern?
When you have code that's asynchronous, you usually have no reason to use Task.Run() (or, even worse, Task.Factory.StartNew()). This means that you can change your code to something like this:
await WorkerMainAsync();
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
while (true)
{
await ss.WaitAsync();
// you should probably store this task somewhere and then await it
var task = DoPollingThenWorkAsync();
}
}
async Task DoPollingThenWorkAsync(SemaphoreSlim semaphore)
{
var msg = Poll();
if (msg != null)
{
await Task.Delay(3000); // process the I/O-bound job
}
// this assumes you don't have to worry about exceptions
// otherwise consider try-finally
semaphore.Release();
}
Usually you don't use async/await inside a CPU-bound task. The method that starts such a task (WorkerMainAsync) can use async/await, but you should be tracking pending tasks:
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
List<Task> trackedTasks = new List<Task>();
while (DoMore())
{
await ss.WaitAsync();
trackedTasks.Add(Task.Run(() =>
{
DoPollingThenWorkAsync();
ss.Release();
}));
}
await Task.WhenAll(trackedTasks);
}
void DoPollingThenWorkAsync()
{
var msg = Poll();
if (msg != null)
{
Thread.Sleep(2000); // process the long running CPU-bound job
}
}
Another exercise would be to remove tasks from trackedTasks as they are finishing. For example, you could use ContinueWith to remove a finished tasks (in this case, remember to use lock to protect trackedTasks from simultaneous access).
If you really need to use await inside DoPollingThenWorkAsync, the code wouldn't change a lot:
trackedTasks.Add(Task.Run(async () =>
{
await DoPollingThenWorkAsync();
ss.Release();
}));
Note that in this case, you'd be dealing with a nested task here for the async lambda, which Task.Run will automatically unwrap for you.

Call long running method and continue with other tasks

public class PerformMainTask()
{
Task1();
Task2();
PerformLongTask();
Task3();
Task4();
}
What I would like to achieve here is to PerformLongTask() onto another thread, and to continue with Task3 & Task4 even when PerformLongTask() is still running.
How should my PerformLongTask() be like in a C# 5.0 way?
Do I need to use async/await?
The simplest solution I know of is:
Task1();
Task2();
var task3 = Task.Run(() => PerformLongTask());
Task4();
Task5();
task3.Wait(); //if task3 has not started yet it will be inlined here
Simple and efficient. If you need to propagate errors you should probably use Parallel.Invoke:
Parallel.Invoke(
() => { PerformLongTask(); },
() => { Task4(); Task5(); }
);
Assuming you are using C# 5 and all your Task() methods truly return a Task (or anything awaitable), your code should look like that:
public async Task PerformMainTask()
{
await Task1();
await Task2();
// Start long task
var longTask = PerformLongTask();
await Task3();
await Task4();
//wait for long task to finish
await longTask;
}
However, if your long task does not run parallelly on its own, you can force it to do so with Task.Run:
public async Task PerformMainTask()
{
await Task1();
await Task2();
// Start long task
var longTask = Task.Run(PerformLongTask);
await Task3();
await Task4();
//wait for long task to finish
await longTask;
}
If none are your tasks are really tasks, just strip all the await except the last one, and you will be good to go.
public class PerformMainTask()
{
Task1();
Task2();
Task.WaitAll(Task.Factory.StartNew(() => PerformLongTask()), Task.Factory.StartNew(() => { Task3(); Task4(); }));
}

Categories

Resources