Async/await in Parallel.ForEach











up vote
2
down vote

favorite
2












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 delegate public 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();
}
}
}









share|improve this question




























    up vote
    2
    down vote

    favorite
    2












    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 delegate public 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();
    }
    }
    }









    share|improve this question


























      up vote
      2
      down vote

      favorite
      2









      up vote
      2
      down vote

      favorite
      2






      2





      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 delegate public 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();
      }
      }
      }









      share|improve this question















      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 delegate public 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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Feb 10 at 4:47









      Nkosi

      2,108619




      2,108619










      asked Feb 9 at 12:35









      Raskolnikov

      3721619




      3721619






















          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.






          share|improve this answer























          • 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, 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




















          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);





          share|improve this answer























          • 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











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          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

























          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.






          share|improve this answer























          • 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, 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

















          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.






          share|improve this answer























          • 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, 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















          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.






          share|improve this answer















          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          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, 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




















          • 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, 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


















          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














          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);





          share|improve this answer























          • 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















          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);





          share|improve this answer























          • 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













          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);





          share|improve this answer














          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);






          share|improve this answer














          share|improve this answer



          share|improve this answer








          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 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


















          • 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
















          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


















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Quarter-circle Tiles

          build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

          Mont Emei