Async/await in Parallel.ForEach
up vote
2
down vote
favorite
I have a Timer
:
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
The important part here is the method QueryReportTimerCallback
.
I have a few doubts here:
- Is it ok to have an
async void
method? In general it is not, but how can I avoid this when the timer callback is delegatepublic delegate void TimerCallback(object state);?
- Is it ok to have await inside
Parallel.ForEach
?
Method QueryReportTimerCallback
:
private async void QueryReportTimerCallback(object state)
{
if (await semaphoreQueryReportSlim.WaitAsync(10))
{
try
{
if (machineConfigurations != null)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(machineConfigurations.Where(x => x.QueryReport), async (configuration) =>
{
if (configuration.IsConnectionValid)
{
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
})
);
}
}
catch (Exception e)
{
logger.LogError(e, e.Message);
}
finally
{
semaphoreQueryReportSlim.Release();
}
}
}
c# async-await
add a comment |
up vote
2
down vote
favorite
I have a Timer
:
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
The important part here is the method QueryReportTimerCallback
.
I have a few doubts here:
- Is it ok to have an
async void
method? In general it is not, but how can I avoid this when the timer callback is delegatepublic delegate void TimerCallback(object state);?
- Is it ok to have await inside
Parallel.ForEach
?
Method QueryReportTimerCallback
:
private async void QueryReportTimerCallback(object state)
{
if (await semaphoreQueryReportSlim.WaitAsync(10))
{
try
{
if (machineConfigurations != null)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(machineConfigurations.Where(x => x.QueryReport), async (configuration) =>
{
if (configuration.IsConnectionValid)
{
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
})
);
}
}
catch (Exception e)
{
logger.LogError(e, e.Message);
}
finally
{
semaphoreQueryReportSlim.Release();
}
}
}
c# async-await
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I have a Timer
:
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
The important part here is the method QueryReportTimerCallback
.
I have a few doubts here:
- Is it ok to have an
async void
method? In general it is not, but how can I avoid this when the timer callback is delegatepublic delegate void TimerCallback(object state);?
- Is it ok to have await inside
Parallel.ForEach
?
Method QueryReportTimerCallback
:
private async void QueryReportTimerCallback(object state)
{
if (await semaphoreQueryReportSlim.WaitAsync(10))
{
try
{
if (machineConfigurations != null)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(machineConfigurations.Where(x => x.QueryReport), async (configuration) =>
{
if (configuration.IsConnectionValid)
{
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
})
);
}
}
catch (Exception e)
{
logger.LogError(e, e.Message);
}
finally
{
semaphoreQueryReportSlim.Release();
}
}
}
c# async-await
I have a Timer
:
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
The important part here is the method QueryReportTimerCallback
.
I have a few doubts here:
- Is it ok to have an
async void
method? In general it is not, but how can I avoid this when the timer callback is delegatepublic delegate void TimerCallback(object state);?
- Is it ok to have await inside
Parallel.ForEach
?
Method QueryReportTimerCallback
:
private async void QueryReportTimerCallback(object state)
{
if (await semaphoreQueryReportSlim.WaitAsync(10))
{
try
{
if (machineConfigurations != null)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(machineConfigurations.Where(x => x.QueryReport), async (configuration) =>
{
if (configuration.IsConnectionValid)
{
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
})
);
}
}
catch (Exception e)
{
logger.LogError(e, e.Message);
}
finally
{
semaphoreQueryReportSlim.Release();
}
}
}
c# async-await
c# async-await
edited Feb 10 at 4:47
Nkosi
2,108619
2,108619
asked Feb 9 at 12:35
Raskolnikov
3721619
3721619
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
up vote
8
down vote
accepted
Is it ok to have an async void method?
Referencing Async/Await - Best Practices in Asynchronous Programming
As already stated in the OP async void
should be avoided as much as possible.
The one exception to that rule being for event handlers, which can be the loophole to achieving the desired behavior while still having the ability to catch and handle any thrown exceptions.
Create an event
event EventHandler QueryReportCallbackEvent = delegate { };
to be raised by the timer callback
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
The event handler allows async void
, so now you can safely do
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
Is it ok to have await inside Parallel.ForEach?
NO!!!!
The async lambda
async (configuration) => ...
will be converted to async void
which takes us right back to what was said in the beginning. async void
BAD!!!
Instead refactor that lambda out into its own method that returns a Task
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
and this will now allow for the use of Task.WhenAll
with all the machine configurations retrieved from the query.
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
This actually removes the need for the Paralell.ForEach
.
Here is the complete code for what was described above.
//CTOR
public MyClass() {
QueryReportCallbackEvent += QueryReportCallbackEventHandler;
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
}
event EventHandler QueryReportCallbackEvent = delegate { };
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
private async Task queryReportsCore() {
try {
if (machineConfigurations != null) {
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
}
} catch (Exception e) {
logger.LogError(e, e.Message);
} finally {
semaphoreQueryReportSlim.Release();
}
}
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
Lastly take note of how the functions were broken down into smaller chunks that allowed for cleaner, more easy to read code.
Thank you for your answer. In my case I swallow an exceptions. Does this mean that I don't need to worry about "async void" in callback method? If we ignore fact that would be nicer, cleaner, more readable and by the book.
– Raskolnikov
Feb 12 at 8:41
@Raskolnikov Where are you swallowing exception? if an exception is thrown in your async ForEach callback it wont get caught by that catch block you have there as it will be on a different thread. async voids are also called fire and forget as there is no telling what happens to them when invoked.
– Nkosi
Feb 12 at 11:57
I am trying to understand what is benefit of using EventHandler? In both cases you will lose information about exception. Since event handler is also async void.
– Raskolnikov
Feb 12 at 12:08
@Raskolnikov as I said before event handlers are the one exception (pardon the pun) to that rule.async void
on event handlers are allowed. Read the reference link in the answer.
– Nkosi
Feb 12 at 12:09
But...Parallel.ForEach
runs something in parallel, whileWhenAll
guarantees that all tasks will be finished by this point of the execution, but DOES NOT guarantees parallelism, both things are way different, am I mistaken? Why would exchange one with another?
– kuskmen
Feb 12 at 14:50
|
show 4 more comments
up vote
1
down vote
I've created an extension method for this which makes use of SemaphoreSlim and also allows to set maximum degree of parallelism
/// <summary>
/// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of IEnumerable</typeparam>
/// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
/// <param name="action">an async <see cref="Action" /> to execute</param>
/// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
/// Must be grater than 0</param>
/// <returns>A Task representing an async operation</returns>
/// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
public static async Task ForEachAsyncConcurrent<T>(
this IEnumerable<T> enumerable,
Func<T, Task> action,
int? maxDegreeOfParallelism = null)
{
if (maxDegreeOfParallelism.HasValue)
{
using (var semaphoreSlim = new SemaphoreSlim(
maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
{
var tasksWithThrottler = new List<Task>();
foreach (var item in enumerable)
{
// Increment the number of currently running tasks and wait if they are more than limit.
await semaphoreSlim.WaitAsync();
tasksWithThrottler.Add(Task.Run(async () =>
{
await action(item).ContinueWith(res =>
{
// action is completed, so decrement the number of currently running tasks
semaphoreSlim.Release();
});
}));
}
// Wait for all tasks to complete.
await Task.WhenAll(tasksWithThrottler.ToArray());
}
}
else
{
await Task.WhenAll(enumerable.Select(item => action(item)));
}
}
Sample Usage:
await enumerable.ForEachAsyncConcurrent(
async item =>
{
await SomeAsyncMethod(item);
},
5);
I really like this solution! It's clear to read and it helped me reduce the run time of my program by 60%. Any recommendations on themaxDegreeOfParallelism
value?
– Stephan
Oct 31 at 21:12
It depends on how many cores you have on your computer and how complex the task is. Usually, 10 is a good starting point and then you can tune up according to your system and requirement.
– Jay Shah
Oct 31 at 22:10
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
8
down vote
accepted
Is it ok to have an async void method?
Referencing Async/Await - Best Practices in Asynchronous Programming
As already stated in the OP async void
should be avoided as much as possible.
The one exception to that rule being for event handlers, which can be the loophole to achieving the desired behavior while still having the ability to catch and handle any thrown exceptions.
Create an event
event EventHandler QueryReportCallbackEvent = delegate { };
to be raised by the timer callback
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
The event handler allows async void
, so now you can safely do
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
Is it ok to have await inside Parallel.ForEach?
NO!!!!
The async lambda
async (configuration) => ...
will be converted to async void
which takes us right back to what was said in the beginning. async void
BAD!!!
Instead refactor that lambda out into its own method that returns a Task
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
and this will now allow for the use of Task.WhenAll
with all the machine configurations retrieved from the query.
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
This actually removes the need for the Paralell.ForEach
.
Here is the complete code for what was described above.
//CTOR
public MyClass() {
QueryReportCallbackEvent += QueryReportCallbackEventHandler;
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
}
event EventHandler QueryReportCallbackEvent = delegate { };
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
private async Task queryReportsCore() {
try {
if (machineConfigurations != null) {
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
}
} catch (Exception e) {
logger.LogError(e, e.Message);
} finally {
semaphoreQueryReportSlim.Release();
}
}
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
Lastly take note of how the functions were broken down into smaller chunks that allowed for cleaner, more easy to read code.
Thank you for your answer. In my case I swallow an exceptions. Does this mean that I don't need to worry about "async void" in callback method? If we ignore fact that would be nicer, cleaner, more readable and by the book.
– Raskolnikov
Feb 12 at 8:41
@Raskolnikov Where are you swallowing exception? if an exception is thrown in your async ForEach callback it wont get caught by that catch block you have there as it will be on a different thread. async voids are also called fire and forget as there is no telling what happens to them when invoked.
– Nkosi
Feb 12 at 11:57
I am trying to understand what is benefit of using EventHandler? In both cases you will lose information about exception. Since event handler is also async void.
– Raskolnikov
Feb 12 at 12:08
@Raskolnikov as I said before event handlers are the one exception (pardon the pun) to that rule.async void
on event handlers are allowed. Read the reference link in the answer.
– Nkosi
Feb 12 at 12:09
But...Parallel.ForEach
runs something in parallel, whileWhenAll
guarantees that all tasks will be finished by this point of the execution, but DOES NOT guarantees parallelism, both things are way different, am I mistaken? Why would exchange one with another?
– kuskmen
Feb 12 at 14:50
|
show 4 more comments
up vote
8
down vote
accepted
Is it ok to have an async void method?
Referencing Async/Await - Best Practices in Asynchronous Programming
As already stated in the OP async void
should be avoided as much as possible.
The one exception to that rule being for event handlers, which can be the loophole to achieving the desired behavior while still having the ability to catch and handle any thrown exceptions.
Create an event
event EventHandler QueryReportCallbackEvent = delegate { };
to be raised by the timer callback
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
The event handler allows async void
, so now you can safely do
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
Is it ok to have await inside Parallel.ForEach?
NO!!!!
The async lambda
async (configuration) => ...
will be converted to async void
which takes us right back to what was said in the beginning. async void
BAD!!!
Instead refactor that lambda out into its own method that returns a Task
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
and this will now allow for the use of Task.WhenAll
with all the machine configurations retrieved from the query.
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
This actually removes the need for the Paralell.ForEach
.
Here is the complete code for what was described above.
//CTOR
public MyClass() {
QueryReportCallbackEvent += QueryReportCallbackEventHandler;
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
}
event EventHandler QueryReportCallbackEvent = delegate { };
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
private async Task queryReportsCore() {
try {
if (machineConfigurations != null) {
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
}
} catch (Exception e) {
logger.LogError(e, e.Message);
} finally {
semaphoreQueryReportSlim.Release();
}
}
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
Lastly take note of how the functions were broken down into smaller chunks that allowed for cleaner, more easy to read code.
Thank you for your answer. In my case I swallow an exceptions. Does this mean that I don't need to worry about "async void" in callback method? If we ignore fact that would be nicer, cleaner, more readable and by the book.
– Raskolnikov
Feb 12 at 8:41
@Raskolnikov Where are you swallowing exception? if an exception is thrown in your async ForEach callback it wont get caught by that catch block you have there as it will be on a different thread. async voids are also called fire and forget as there is no telling what happens to them when invoked.
– Nkosi
Feb 12 at 11:57
I am trying to understand what is benefit of using EventHandler? In both cases you will lose information about exception. Since event handler is also async void.
– Raskolnikov
Feb 12 at 12:08
@Raskolnikov as I said before event handlers are the one exception (pardon the pun) to that rule.async void
on event handlers are allowed. Read the reference link in the answer.
– Nkosi
Feb 12 at 12:09
But...Parallel.ForEach
runs something in parallel, whileWhenAll
guarantees that all tasks will be finished by this point of the execution, but DOES NOT guarantees parallelism, both things are way different, am I mistaken? Why would exchange one with another?
– kuskmen
Feb 12 at 14:50
|
show 4 more comments
up vote
8
down vote
accepted
up vote
8
down vote
accepted
Is it ok to have an async void method?
Referencing Async/Await - Best Practices in Asynchronous Programming
As already stated in the OP async void
should be avoided as much as possible.
The one exception to that rule being for event handlers, which can be the loophole to achieving the desired behavior while still having the ability to catch and handle any thrown exceptions.
Create an event
event EventHandler QueryReportCallbackEvent = delegate { };
to be raised by the timer callback
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
The event handler allows async void
, so now you can safely do
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
Is it ok to have await inside Parallel.ForEach?
NO!!!!
The async lambda
async (configuration) => ...
will be converted to async void
which takes us right back to what was said in the beginning. async void
BAD!!!
Instead refactor that lambda out into its own method that returns a Task
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
and this will now allow for the use of Task.WhenAll
with all the machine configurations retrieved from the query.
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
This actually removes the need for the Paralell.ForEach
.
Here is the complete code for what was described above.
//CTOR
public MyClass() {
QueryReportCallbackEvent += QueryReportCallbackEventHandler;
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
}
event EventHandler QueryReportCallbackEvent = delegate { };
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
private async Task queryReportsCore() {
try {
if (machineConfigurations != null) {
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
}
} catch (Exception e) {
logger.LogError(e, e.Message);
} finally {
semaphoreQueryReportSlim.Release();
}
}
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
Lastly take note of how the functions were broken down into smaller chunks that allowed for cleaner, more easy to read code.
Is it ok to have an async void method?
Referencing Async/Await - Best Practices in Asynchronous Programming
As already stated in the OP async void
should be avoided as much as possible.
The one exception to that rule being for event handlers, which can be the loophole to achieving the desired behavior while still having the ability to catch and handle any thrown exceptions.
Create an event
event EventHandler QueryReportCallbackEvent = delegate { };
to be raised by the timer callback
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
The event handler allows async void
, so now you can safely do
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
Is it ok to have await inside Parallel.ForEach?
NO!!!!
The async lambda
async (configuration) => ...
will be converted to async void
which takes us right back to what was said in the beginning. async void
BAD!!!
Instead refactor that lambda out into its own method that returns a Task
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
and this will now allow for the use of Task.WhenAll
with all the machine configurations retrieved from the query.
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
This actually removes the need for the Paralell.ForEach
.
Here is the complete code for what was described above.
//CTOR
public MyClass() {
QueryReportCallbackEvent += QueryReportCallbackEventHandler;
var QueryReportTimer = new Timer(QueryReportTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
}
event EventHandler QueryReportCallbackEvent = delegate { };
private void QueryReportTimerCallback(object state) {
QueryReportCallbackEvent(this, EventArgs.Empty);
}
private async void QueryReportCallbackEventHandler(object sender, EventArgs e) {
if (await semaphoreQueryReportSlim.WaitAsync(10))
await queryReportsCore();
}
private async Task queryReportsCore() {
try {
if (machineConfigurations != null) {
var tasks = machineConfigurations
.Where(x => x.QueryReport)
.Select(configuration => HandleReport(configuration));
await Task.WhenAll(tasks);
}
} catch (Exception e) {
logger.LogError(e, e.Message);
} finally {
semaphoreQueryReportSlim.Release();
}
}
private async Task HandleReport(MachineConfiguration configuration) {
if (configuration.IsConnectionValid) {
var queryReport = new QueryReport(configuration, ReportConfigurations, fileContainer, applicationConfiguration, logger);
await QueryAReport(configuration, queryReport);
}
}
Lastly take note of how the functions were broken down into smaller chunks that allowed for cleaner, more easy to read code.
edited Aug 6 at 21:44
answered Feb 9 at 23:21
Nkosi
2,108619
2,108619
Thank you for your answer. In my case I swallow an exceptions. Does this mean that I don't need to worry about "async void" in callback method? If we ignore fact that would be nicer, cleaner, more readable and by the book.
– Raskolnikov
Feb 12 at 8:41
@Raskolnikov Where are you swallowing exception? if an exception is thrown in your async ForEach callback it wont get caught by that catch block you have there as it will be on a different thread. async voids are also called fire and forget as there is no telling what happens to them when invoked.
– Nkosi
Feb 12 at 11:57
I am trying to understand what is benefit of using EventHandler? In both cases you will lose information about exception. Since event handler is also async void.
– Raskolnikov
Feb 12 at 12:08
@Raskolnikov as I said before event handlers are the one exception (pardon the pun) to that rule.async void
on event handlers are allowed. Read the reference link in the answer.
– Nkosi
Feb 12 at 12:09
But...Parallel.ForEach
runs something in parallel, whileWhenAll
guarantees that all tasks will be finished by this point of the execution, but DOES NOT guarantees parallelism, both things are way different, am I mistaken? Why would exchange one with another?
– kuskmen
Feb 12 at 14:50
|
show 4 more comments
Thank you for your answer. In my case I swallow an exceptions. Does this mean that I don't need to worry about "async void" in callback method? If we ignore fact that would be nicer, cleaner, more readable and by the book.
– Raskolnikov
Feb 12 at 8:41
@Raskolnikov Where are you swallowing exception? if an exception is thrown in your async ForEach callback it wont get caught by that catch block you have there as it will be on a different thread. async voids are also called fire and forget as there is no telling what happens to them when invoked.
– Nkosi
Feb 12 at 11:57
I am trying to understand what is benefit of using EventHandler? In both cases you will lose information about exception. Since event handler is also async void.
– Raskolnikov
Feb 12 at 12:08
@Raskolnikov as I said before event handlers are the one exception (pardon the pun) to that rule.async void
on event handlers are allowed. Read the reference link in the answer.
– Nkosi
Feb 12 at 12:09
But...Parallel.ForEach
runs something in parallel, whileWhenAll
guarantees that all tasks will be finished by this point of the execution, but DOES NOT guarantees parallelism, both things are way different, am I mistaken? Why would exchange one with another?
– kuskmen
Feb 12 at 14:50
Thank you for your answer. In my case I swallow an exceptions. Does this mean that I don't need to worry about "async void" in callback method? If we ignore fact that would be nicer, cleaner, more readable and by the book.
– Raskolnikov
Feb 12 at 8:41
Thank you for your answer. In my case I swallow an exceptions. Does this mean that I don't need to worry about "async void" in callback method? If we ignore fact that would be nicer, cleaner, more readable and by the book.
– Raskolnikov
Feb 12 at 8:41
@Raskolnikov Where are you swallowing exception? if an exception is thrown in your async ForEach callback it wont get caught by that catch block you have there as it will be on a different thread. async voids are also called fire and forget as there is no telling what happens to them when invoked.
– Nkosi
Feb 12 at 11:57
@Raskolnikov Where are you swallowing exception? if an exception is thrown in your async ForEach callback it wont get caught by that catch block you have there as it will be on a different thread. async voids are also called fire and forget as there is no telling what happens to them when invoked.
– Nkosi
Feb 12 at 11:57
I am trying to understand what is benefit of using EventHandler? In both cases you will lose information about exception. Since event handler is also async void.
– Raskolnikov
Feb 12 at 12:08
I am trying to understand what is benefit of using EventHandler? In both cases you will lose information about exception. Since event handler is also async void.
– Raskolnikov
Feb 12 at 12:08
@Raskolnikov as I said before event handlers are the one exception (pardon the pun) to that rule.
async void
on event handlers are allowed. Read the reference link in the answer.– Nkosi
Feb 12 at 12:09
@Raskolnikov as I said before event handlers are the one exception (pardon the pun) to that rule.
async void
on event handlers are allowed. Read the reference link in the answer.– Nkosi
Feb 12 at 12:09
But...
Parallel.ForEach
runs something in parallel, while WhenAll
guarantees that all tasks will be finished by this point of the execution, but DOES NOT guarantees parallelism, both things are way different, am I mistaken? Why would exchange one with another?– kuskmen
Feb 12 at 14:50
But...
Parallel.ForEach
runs something in parallel, while WhenAll
guarantees that all tasks will be finished by this point of the execution, but DOES NOT guarantees parallelism, both things are way different, am I mistaken? Why would exchange one with another?– kuskmen
Feb 12 at 14:50
|
show 4 more comments
up vote
1
down vote
I've created an extension method for this which makes use of SemaphoreSlim and also allows to set maximum degree of parallelism
/// <summary>
/// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of IEnumerable</typeparam>
/// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
/// <param name="action">an async <see cref="Action" /> to execute</param>
/// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
/// Must be grater than 0</param>
/// <returns>A Task representing an async operation</returns>
/// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
public static async Task ForEachAsyncConcurrent<T>(
this IEnumerable<T> enumerable,
Func<T, Task> action,
int? maxDegreeOfParallelism = null)
{
if (maxDegreeOfParallelism.HasValue)
{
using (var semaphoreSlim = new SemaphoreSlim(
maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
{
var tasksWithThrottler = new List<Task>();
foreach (var item in enumerable)
{
// Increment the number of currently running tasks and wait if they are more than limit.
await semaphoreSlim.WaitAsync();
tasksWithThrottler.Add(Task.Run(async () =>
{
await action(item).ContinueWith(res =>
{
// action is completed, so decrement the number of currently running tasks
semaphoreSlim.Release();
});
}));
}
// Wait for all tasks to complete.
await Task.WhenAll(tasksWithThrottler.ToArray());
}
}
else
{
await Task.WhenAll(enumerable.Select(item => action(item)));
}
}
Sample Usage:
await enumerable.ForEachAsyncConcurrent(
async item =>
{
await SomeAsyncMethod(item);
},
5);
I really like this solution! It's clear to read and it helped me reduce the run time of my program by 60%. Any recommendations on themaxDegreeOfParallelism
value?
– Stephan
Oct 31 at 21:12
It depends on how many cores you have on your computer and how complex the task is. Usually, 10 is a good starting point and then you can tune up according to your system and requirement.
– Jay Shah
Oct 31 at 22:10
add a comment |
up vote
1
down vote
I've created an extension method for this which makes use of SemaphoreSlim and also allows to set maximum degree of parallelism
/// <summary>
/// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of IEnumerable</typeparam>
/// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
/// <param name="action">an async <see cref="Action" /> to execute</param>
/// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
/// Must be grater than 0</param>
/// <returns>A Task representing an async operation</returns>
/// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
public static async Task ForEachAsyncConcurrent<T>(
this IEnumerable<T> enumerable,
Func<T, Task> action,
int? maxDegreeOfParallelism = null)
{
if (maxDegreeOfParallelism.HasValue)
{
using (var semaphoreSlim = new SemaphoreSlim(
maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
{
var tasksWithThrottler = new List<Task>();
foreach (var item in enumerable)
{
// Increment the number of currently running tasks and wait if they are more than limit.
await semaphoreSlim.WaitAsync();
tasksWithThrottler.Add(Task.Run(async () =>
{
await action(item).ContinueWith(res =>
{
// action is completed, so decrement the number of currently running tasks
semaphoreSlim.Release();
});
}));
}
// Wait for all tasks to complete.
await Task.WhenAll(tasksWithThrottler.ToArray());
}
}
else
{
await Task.WhenAll(enumerable.Select(item => action(item)));
}
}
Sample Usage:
await enumerable.ForEachAsyncConcurrent(
async item =>
{
await SomeAsyncMethod(item);
},
5);
I really like this solution! It's clear to read and it helped me reduce the run time of my program by 60%. Any recommendations on themaxDegreeOfParallelism
value?
– Stephan
Oct 31 at 21:12
It depends on how many cores you have on your computer and how complex the task is. Usually, 10 is a good starting point and then you can tune up according to your system and requirement.
– Jay Shah
Oct 31 at 22:10
add a comment |
up vote
1
down vote
up vote
1
down vote
I've created an extension method for this which makes use of SemaphoreSlim and also allows to set maximum degree of parallelism
/// <summary>
/// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of IEnumerable</typeparam>
/// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
/// <param name="action">an async <see cref="Action" /> to execute</param>
/// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
/// Must be grater than 0</param>
/// <returns>A Task representing an async operation</returns>
/// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
public static async Task ForEachAsyncConcurrent<T>(
this IEnumerable<T> enumerable,
Func<T, Task> action,
int? maxDegreeOfParallelism = null)
{
if (maxDegreeOfParallelism.HasValue)
{
using (var semaphoreSlim = new SemaphoreSlim(
maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
{
var tasksWithThrottler = new List<Task>();
foreach (var item in enumerable)
{
// Increment the number of currently running tasks and wait if they are more than limit.
await semaphoreSlim.WaitAsync();
tasksWithThrottler.Add(Task.Run(async () =>
{
await action(item).ContinueWith(res =>
{
// action is completed, so decrement the number of currently running tasks
semaphoreSlim.Release();
});
}));
}
// Wait for all tasks to complete.
await Task.WhenAll(tasksWithThrottler.ToArray());
}
}
else
{
await Task.WhenAll(enumerable.Select(item => action(item)));
}
}
Sample Usage:
await enumerable.ForEachAsyncConcurrent(
async item =>
{
await SomeAsyncMethod(item);
},
5);
I've created an extension method for this which makes use of SemaphoreSlim and also allows to set maximum degree of parallelism
/// <summary>
/// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of IEnumerable</typeparam>
/// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
/// <param name="action">an async <see cref="Action" /> to execute</param>
/// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
/// Must be grater than 0</param>
/// <returns>A Task representing an async operation</returns>
/// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
public static async Task ForEachAsyncConcurrent<T>(
this IEnumerable<T> enumerable,
Func<T, Task> action,
int? maxDegreeOfParallelism = null)
{
if (maxDegreeOfParallelism.HasValue)
{
using (var semaphoreSlim = new SemaphoreSlim(
maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
{
var tasksWithThrottler = new List<Task>();
foreach (var item in enumerable)
{
// Increment the number of currently running tasks and wait if they are more than limit.
await semaphoreSlim.WaitAsync();
tasksWithThrottler.Add(Task.Run(async () =>
{
await action(item).ContinueWith(res =>
{
// action is completed, so decrement the number of currently running tasks
semaphoreSlim.Release();
});
}));
}
// Wait for all tasks to complete.
await Task.WhenAll(tasksWithThrottler.ToArray());
}
}
else
{
await Task.WhenAll(enumerable.Select(item => action(item)));
}
}
Sample Usage:
await enumerable.ForEachAsyncConcurrent(
async item =>
{
await SomeAsyncMethod(item);
},
5);
edited 2 days ago
answered May 9 at 22:47
Jay Shah
1452
1452
I really like this solution! It's clear to read and it helped me reduce the run time of my program by 60%. Any recommendations on themaxDegreeOfParallelism
value?
– Stephan
Oct 31 at 21:12
It depends on how many cores you have on your computer and how complex the task is. Usually, 10 is a good starting point and then you can tune up according to your system and requirement.
– Jay Shah
Oct 31 at 22:10
add a comment |
I really like this solution! It's clear to read and it helped me reduce the run time of my program by 60%. Any recommendations on themaxDegreeOfParallelism
value?
– Stephan
Oct 31 at 21:12
It depends on how many cores you have on your computer and how complex the task is. Usually, 10 is a good starting point and then you can tune up according to your system and requirement.
– Jay Shah
Oct 31 at 22:10
I really like this solution! It's clear to read and it helped me reduce the run time of my program by 60%. Any recommendations on the
maxDegreeOfParallelism
value?– Stephan
Oct 31 at 21:12
I really like this solution! It's clear to read and it helped me reduce the run time of my program by 60%. Any recommendations on the
maxDegreeOfParallelism
value?– Stephan
Oct 31 at 21:12
It depends on how many cores you have on your computer and how complex the task is. Usually, 10 is a good starting point and then you can tune up according to your system and requirement.
– Jay Shah
Oct 31 at 22:10
It depends on how many cores you have on your computer and how complex the task is. Usually, 10 is a good starting point and then you can tune up according to your system and requirement.
– Jay Shah
Oct 31 at 22:10
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187181%2fasync-await-in-parallel-foreach%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown