1

I am working on some legacy code which repeatedly calls a long running task in a new thread:

var jobList = spGetSomeJobIds.ToList();

jobList.ForEach((jobId) =>
{
    var myTask = Task.Factory.StartNew(() => CallExpensiveStoredProc(jobId), 
        TaskCreationOptions.LongRunning);
    myTask.Wait();
});

As the calling thread immediately calls Wait and blocks until the task completes I can't see any point in the Task.Factory.StartNew code. Am I missing something? is there something about TaskCreationOptions.LongRunning which might add value?

StepUp
  • 36,391
  • 15
  • 88
  • 148
jonnarosey
  • 520
  • 1
  • 8
  • 19
  • If it did an async `await` rather than that `Wait` then there are certain contexts (freeing the UI thread) where it can make sense. But doing a blocking wait, I wouldn't have thought so. – Damien_The_Unbeliever Jan 27 '20 at 09:51
  • It would be a way of ensuring that `CallExpensiveStoredProc` runs on a separate thread and no two invocations of `CallExpensiveStoredProc` happen at the same time. – Enigmativity Jan 27 '20 at 09:58
  • @Enigmativity I should have added this is a console application and only one instance will ever be run, given that constraint do you see any value in the `Task.Factory.StartNew` code? – jonnarosey Jan 27 '20 at 10:06
  • 1
    @jonaglon - Background threads will also exit cleanly if you close the app. It might be something to do with that. – Enigmativity Jan 27 '20 at 10:13
  • 1
    There is no point whatsoever in my opinion. – Theodor Zoulias Jan 27 '20 at 11:02
  • @Enigmativity are you sure background threads will exit cleanly? This answer suggests that's not correct https://stackoverflow.com/questions/32261314/does-the-task-still-survive-when-the-main-thread-that-creates-it-has-been-killed – jonnarosey Jan 27 '20 at 11:47
  • Why wouldn't the main task just call CallExpensiveStoredProc directly? – Lasse V. Karlsen Jan 28 '20 at 10:24

1 Answers1

-2

As msdn says:

Waits for the Task to complete execution.

in addition, there is the following statement:

Wait blocks the calling thread until the task completes.

So myTask.Wait(); looks redundant as method CallExpensiveStoredProc returns nothing.

As a good practise, it would be better to use async and await operators when you deal with asyncronous operations such as database operations.

UPDATE:

What we have is:

  1. We run LongRunning, so new Thread is created. It can be seen in source files.

  2. Then we call myTask.Wait();. This method just waits when myTask will finish its work. So all jobList iterations will be executed sequantially, not parallely. So now we need to decide how our job should be executed - sequantially(case A) or parallelly(case B).

Case A: Sequantial execution of our jobs

If your jobs should be executed sequntially, then a few questions might be arisen:

  • what for do we use multithreading, if our code is executing sequantially? Our code should be clean and simple. So we can avoid using multithreading in this case
  • when we create a new thread, we are adding additional overheads to the threadpool. Because thread pool tries to determine the optimal number of threads and it creates at least one thread per core. That means when all of the thread pool threads are busy, the task might wait (in extreme cases infinitely long), until it actually starts executing.

To sum up, so there is no gain in this case to create new Thread, especially new thread using LongRunning enum.

Case B: Parallel execution of our jobs

If our goal is to run all jobs parallely, then myTask.Wait(); should be eliminated because it makes code to be executed sequntially.

Code to test:

var jobs = new List<int>(){1, 2, 3 };
jobs.ForEach(j => 
   {
       var myTask = Task.Factory.StartNew(() => 
       {
           Console.WriteLine($"This is a current number of executing task: { j }");
           Thread.Sleep(5000); // Imitation of long-running operation
           Console.WriteLine($"Executed: { j }");
       },  TaskCreationOptions.LongRunning);
       myTask.Wait();
    });
Console.WriteLine($"All jobs are executed");

To conclude in this case B, there is no gain to create new Thread, especially new thread using LongRunning enum. Because this is an expensive operation in the time it takes to be created and in memory consumption.

Community
  • 1
  • 1
StepUp
  • 36,391
  • 15
  • 88
  • 148
  • 1
    There is nothing in this answer that directly addresses the question, "Is there any point starting a LongRunning Task if the parent immediately Waits?" – Enigmativity Jan 27 '20 at 12:05
  • @Enigmativity thanks for the response. However, I've written `So myTask.Wait(); looks redundant as method CallExpensiveStoredProc returns nothing.`. Is it not appropriate? – StepUp Jan 27 '20 at 12:07
  • That statement doesn't answer the question. In fact it's actually untrue. It's perfectly fine for a task to return nothing and have `.Wait()` be called on it. – Enigmativity Jan 27 '20 at 21:29
  • @Enigmativity thanks for the response! I am sorry, but `.Wait()` will block the calling thread until the task completes. Could you say the benefit of this? – StepUp Jan 28 '20 at 07:27
  • But you answer doesn't say what the benefit is or isn't. The question is "Is there any point starting a LongRunning Task if the parent immediately Waits?" You haven't answer it. – Enigmativity Jan 28 '20 at 08:42
  • I think it's getting closer, but I'm now confused with your argument about the `ThreadPool` and the use of the `LongRunning` `enum`. When you use `LongRunning` the `ThreadPool` isn't used so there should be no delay in creating a `Thread`, it just might be an expensive exercise to spin one up though. – Enigmativity Jan 28 '20 at 11:02
  • @downvoter what's reason to downvote? It would be really great to know a reason to improve my answer. Thanks – StepUp Jan 28 '20 at 14:25