Skip to main content
Code Review

Return to Question

replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

Since the API calls are not very CPU intensive (mostly waiting for web responses), Based on Servy's answer Servy's answer, I kept it simple by using async/await rather than using threads, but I may be wrong.

Since the API calls are not very CPU intensive (mostly waiting for web responses), Based on Servy's answer, I kept it simple by using async/await rather than using threads, but I may be wrong.

Since the API calls are not very CPU intensive (mostly waiting for web responses), Based on Servy's answer, I kept it simple by using async/await rather than using threads, but I may be wrong.

follow up questions should be asked in a new question, not edited into the original question.
Source Link


EDIT:

I added a cancellation token and a "task execution time" measurement to adjust the delayed release of the semaphore.

Note that I had to put the TaskUtilities.RetryOnFailure() content inside the Enqueue T method for more convenience, but you might not find this really clean. I should certainly rewrite this in a separate method.

Side question: why externalized it to the TaskUtility class rather than a private TaskQueue's method (since it's not used elsewhere) ?

 public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
 {
 // Acquire a new cancellation source if none or if the previous was cancelled.
 if (source == null || source.IsCancellationRequested)
 {
 source = new CancellationTokenSource();
 }
 // Only one task can be running at one time.
 await semaphore.WaitAsync(source.Token);
 // Replacement of TaskUtilities.RetryOnFailure
 DateTime dateStart = DateTime.Now;
 try
 {
 int maxRetryCount = NumberOfRetries;
 while (true)
 {
 try
 {
 return await taskGenerator();
 }
 catch(Exception ex)
 {
 if (maxRetryCount == 0)
 {
 // Cancel pending tasks.
 if (CancelQueueOnFailure)
 {
 source.Cancel();
 }
 // Rethrow the current exception.
 throw;
 }
 maxRetryCount--;
 }
 // Wait a bit if the task finished faster than the specified minimum duration.
 TimeSpan delay = TimeBetweenTaskExecutions - (DateTime.Now - dateStart);
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay);
 }
 dateStart = DateTime.Now;
 }
 }
 finally
 {
 ReleaseAfterDelay(TimeBetweenTaskExecutions - (DateTime.Now - dateStart));
 }
 }
 /// <summary>
 /// Release the semaphore after a given delay.
 /// </summary>
 private async void ReleaseAfterDelay(TimeSpan delay)
 {
 try
 {
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay).ConfigureAwait(false);
 }
 }
 finally
 {
 semaphore.Release();
 }
 }

Also, for specific needs, I want to manually process the queue (when idle), after all tasks were enqueued. Would you recommend to create a separate class, or just use semaphore = new SemaphoreSlim(0, 1) and semaphore.Release(1) ?

The first case allows me to instantiate the CancellationTokenSource as I release the semaphore, making my previous code cleaner.

The second one could need a "pending tasks" counter to prevent multiple releasings, but that clearly denies your logic separation advice.



EDIT:

I added a cancellation token and a "task execution time" measurement to adjust the delayed release of the semaphore.

Note that I had to put the TaskUtilities.RetryOnFailure() content inside the Enqueue T method for more convenience, but you might not find this really clean. I should certainly rewrite this in a separate method.

Side question: why externalized it to the TaskUtility class rather than a private TaskQueue's method (since it's not used elsewhere) ?

 public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
 {
 // Acquire a new cancellation source if none or if the previous was cancelled.
 if (source == null || source.IsCancellationRequested)
 {
 source = new CancellationTokenSource();
 }
 // Only one task can be running at one time.
 await semaphore.WaitAsync(source.Token);
 // Replacement of TaskUtilities.RetryOnFailure
 DateTime dateStart = DateTime.Now;
 try
 {
 int maxRetryCount = NumberOfRetries;
 while (true)
 {
 try
 {
 return await taskGenerator();
 }
 catch(Exception ex)
 {
 if (maxRetryCount == 0)
 {
 // Cancel pending tasks.
 if (CancelQueueOnFailure)
 {
 source.Cancel();
 }
 // Rethrow the current exception.
 throw;
 }
 maxRetryCount--;
 }
 // Wait a bit if the task finished faster than the specified minimum duration.
 TimeSpan delay = TimeBetweenTaskExecutions - (DateTime.Now - dateStart);
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay);
 }
 dateStart = DateTime.Now;
 }
 }
 finally
 {
 ReleaseAfterDelay(TimeBetweenTaskExecutions - (DateTime.Now - dateStart));
 }
 }
 /// <summary>
 /// Release the semaphore after a given delay.
 /// </summary>
 private async void ReleaseAfterDelay(TimeSpan delay)
 {
 try
 {
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay).ConfigureAwait(false);
 }
 }
 finally
 {
 semaphore.Release();
 }
 }

Also, for specific needs, I want to manually process the queue (when idle), after all tasks were enqueued. Would you recommend to create a separate class, or just use semaphore = new SemaphoreSlim(0, 1) and semaphore.Release(1) ?

The first case allows me to instantiate the CancellationTokenSource as I release the semaphore, making my previous code cleaner.

The second one could need a "pending tasks" counter to prevent multiple releasings, but that clearly denies your logic separation advice.

additional code
Source Link
Profet
  • 103
  • 7


EDIT:

I added a cancellation token and a "task execution time" measurement to adjust the delayed release of the semaphore.

Note that I had to put the TaskUtilities.RetryOnFailure() content inside the Enqueue T method for more convenience, but you might not find this really clean. I should certainly rewrite this in a separate method.

Side question: why externalized it to the TaskUtility class rather than a private TaskQueue's method (since it's not used elsewhere) ?

 public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
 {
 // Acquire a new cancellation source if none or if the previous was cancelled.
 if (source == null || source.IsCancellationRequested)
 {
 source = new CancellationTokenSource();
 }
 // Only one task can be running at one time.
 await semaphore.WaitAsync(source.Token);
 // Replacement of TaskUtilities.RetryOnFailure
 DateTime dateStart = DateTime.Now;
 try
 {
 int maxRetryCount = NumberOfRetries;
 while (true)
 {
 try
 {
 return await taskGenerator();
 }
 catch(Exception ex)
 {
 if (maxRetryCount == 0)
 {
 // Cancel pending tasks.
 if (CancelQueueOnFailure)
 {
 source.Cancel();
 }
 // Rethrow the current exception.
 throw;
 }
 maxRetryCount--;
 }
 // Wait a bit if the task finished faster than the specified minimum duration.
 TimeSpan delay = TimeBetweenTaskExecutions - (DateTime.Now - dateStart);
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay);
 }
 dateStart = DateTime.Now;
 }
 }
 finally
 {
 ReleaseAfterDelay(TimeBetweenTaskExecutions - (DateTime.Now - dateStart));
 }
 }
 /// <summary>
 /// Release the semaphore after a given delay.
 /// </summary>
 private async void ReleaseAfterDelay(TimeSpan delay)
 {
 try
 {
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay).ConfigureAwait(false);
 }
 }
 finally
 {
 semaphore.Release();
 }
 }

Also, for specific needs, I want to manually process the queue (when idle), after all tasks were enqueued. Would you recommend to create a separate class, or just use semaphore = new SemaphoreSlim(0, 1) and semaphore.Release(1) ?

The first case allows me to instantiate the CancellationTokenSource as I release the semaphore, making my previous code cleaner.

The second one could need a "pending tasks" counter to prevent multiple releasings, but that clearly denies your logic separation advice.



EDIT:

I added a cancellation token and a "task execution time" measurement to adjust the delayed release of the semaphore.

Note that I had to put the TaskUtilities.RetryOnFailure() content inside the Enqueue T method for more convenience, but you might not find this really clean. I should certainly rewrite this in a separate method.

Side question: why externalized it to the TaskUtility class rather than a private TaskQueue's method (since it's not used elsewhere) ?

 public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
 {
 // Acquire a new cancellation source if none or if the previous was cancelled.
 if (source == null || source.IsCancellationRequested)
 {
 source = new CancellationTokenSource();
 }
 // Only one task can be running at one time.
 await semaphore.WaitAsync(source.Token);
 // Replacement of TaskUtilities.RetryOnFailure
 DateTime dateStart = DateTime.Now;
 try
 {
 int maxRetryCount = NumberOfRetries;
 while (true)
 {
 try
 {
 return await taskGenerator();
 }
 catch(Exception ex)
 {
 if (maxRetryCount == 0)
 {
 // Cancel pending tasks.
 if (CancelQueueOnFailure)
 {
 source.Cancel();
 }
 // Rethrow the current exception.
 throw;
 }
 maxRetryCount--;
 }
 // Wait a bit if the task finished faster than the specified minimum duration.
 TimeSpan delay = TimeBetweenTaskExecutions - (DateTime.Now - dateStart);
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay);
 }
 dateStart = DateTime.Now;
 }
 }
 finally
 {
 ReleaseAfterDelay(TimeBetweenTaskExecutions - (DateTime.Now - dateStart));
 }
 }
 /// <summary>
 /// Release the semaphore after a given delay.
 /// </summary>
 private async void ReleaseAfterDelay(TimeSpan delay)
 {
 try
 {
 if (delay.TotalMilliseconds > 0)
 {
 await Task.Delay(delay).ConfigureAwait(false);
 }
 }
 finally
 {
 semaphore.Release();
 }
 }

Also, for specific needs, I want to manually process the queue (when idle), after all tasks were enqueued. Would you recommend to create a separate class, or just use semaphore = new SemaphoreSlim(0, 1) and semaphore.Release(1) ?

The first case allows me to instantiate the CancellationTokenSource as I release the semaphore, making my previous code cleaner.

The second one could need a "pending tasks" counter to prevent multiple releasings, but that clearly denies your logic separation advice.

edited body
Source Link
Profet
  • 103
  • 7
Loading
added 50 characters in body
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238
Loading
Source Link
Profet
  • 103
  • 7
Loading
lang-cs

AltStyle によって変換されたページ (->オリジナル) /