Multithreading a console application (the easy way)

So let's say you have something you want to run in a console application, perhaps something with a few jobs for an application.  So simple answer is, write some code in the Program.cs and away you go...  Now, let's say you need to do three different things and for performance reasons they can all be done simultaneously and when they all are completed, you want to execute something to know they are all done.  There are a lot of ways to do this with DI or with different toolkits, but without the full pop and circumstance, here is a simple method to do this.  The only usings you need for this are System; System.Text; System.Threading; and System.Threading.Tasks;.

So the first thing you need to do is create a class to hold the threads along with the function that needs to run for the thread and a completed flag to know when the task is completed.  Here is an example below.  This one also has some basic logging included so that if one of the functions fail, it can be logged properly.

  1. public class Threadder
  2. {
  3. private Thread thread;
  4. private Action action;
  5. private Action runAfterComplete;
  6. private Func<Task> func;
  7. private ILogger logger;
  8.  
  9. public bool HasCompletedThread { get; private set; } = false;
  10.  
  11. public void ThreadStart()
  12. {
  13. thread.Start();
  14. }
  15.  
  16. private void Run()
  17. {
  18. try
  19. {
  20. action();
  21. }
  22. catch (Exception ex)
  23. {
  24. logger.LogCritical(ex, $"Error processing {action.Method.Name}");
  25. }
  26. finally
  27. {
  28. CompleteThread();
  29. }
  30. }
  31.  
  32. private void RunAsync()
  33. {
  34. try
  35. {
  36. func().Wait();
  37. }
  38. catch (Exception ex)
  39. {
  40. logger.LogCritical(ex, $"Error processing {func.Method.Name}");
  41. }
  42. finally
  43. {
  44. CompleteThread();
  45. }
  46. }
  47.  
  48. private void CompleteThread()
  49. {
  50. HasCompletedThread = true;
  51. runAfterComplete();
  52. }
  53.  
  54. public static Threadder CreateFrom(Action action, Action runAfterComplete)
  55. {
  56. var threadder = new Threadder
  57. {
  58. action = action,
  59. runAfterComplete = runAfterComplete,
  60. logger = EmailLogger.CreateFrom()
  61. };
  62. threadder.thread = new Thread(threadder.Run);
  63. return threadder;
  64. }
  65. public static Threadder CreateFrom(Func<Task> func, Action runAfterComplete)
  66. {
  67. var threadder = new Threadder
  68. {
  69. func = func,
  70. runAfterComplete = runAfterComplete,
  71. logger = EmailLogger.CreateFrom()
  72. };
  73. threadder.thread = new Thread(threadder.RunAsync);
  74. return threadder;
  75. }
  76. }

Then on the Program.cs class create an IEnumerable with the list of functions to be called by implementing Threader.CreateFrom(MethodName,SomeMethodToExcecuteUponCompletionOfAllThreads);

Finally, loop through the IEnumerable of threadders calling ThreadStart(); on each item. This will start each of the threads. The sample method for calling when all threads is completed is below

  1. private static void CallAfterThreadComplete()
  2. {
  3. ILogger logger = EmailLogger.CreateFrom();
  4. if (threads.All(t => t.HasCompletedThread))
  5. {
  6. logger.LogInformation("Closing in 5 seconds");
  7. System.Threading.Thread.Sleep(5000);
  8. }
  9. }
Tag: