2

While reading up on multi-threading in C# (both from MSDocs, and books like Concurrency in C# by Stephen Cleary), I have repeatedly come across advice that essentially boils down to: threads are older, low-level, abstractions for concurrency that are superseded by Task and Task<T> classes.

Now I understand that tasks are higher level, more feature rich and powerful, and can do almost anything that threads were previously used for like asynchrony and parallelism.

My question is: Is there anything that threads can do , that newer Task and Task<T> etc. cannot do so that I spend time learning multi-threading just in case I come across those use cases?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
explorer
  • 11,710
  • 5
  • 32
  • 39
  • 1
    A `Task`/`Task` involves creating a `Thread` behind the scenes in multiple situations. The former is just a carefully designed wrapper for the latter, which makes it much simpler to use multi-threading correctly. If you want to really understand how everything works, start with Threads, see how painful they are to use and then switch to learn about Tasks, when and **how** to use them (especially around `async`/`await`). – Camilo Terevinto Sep 18 '21 at 08:10
  • @CamiloTerevinto Thanks for the comment ... so can I deduce from your comment that for routine general purpose high level programming (like end user apps, business apps, higher level libraries etc. ) we will never have to deal with threads directly if we used Task based asynchronous/parallel programming models ? In other words , I should play with threads so I know how they work -- but I will never need them in usual programming scenarios ?? – explorer Sep 18 '21 at 08:18
  • 1
    That's pretty much correct, yes. You should never require Threads in common LOB applications. You'd go back to Threads when you have strict requirements about the lifetime and other aspects of the Thread. Usually, you can build full apps only with `async` and `await` (done correctly, read Stephen Cleary's StackOverflow posts and his blog for good information) – Camilo Terevinto Sep 18 '21 at 08:27
  • 1
    I would use a thread instead of task or a threadpool thread, if I wanted to change the [Thread.Priority](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.priority?view=net-5.0) or processor affinity/IO-priority (with pinvoke) or set the [AppartmentState](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.setapartmentstate?view=net-5.0#System_Threading_Thread_SetApartmentState_System_Threading_ApartmentState_). – Steeeve Sep 18 '21 at 08:43
  • There are things that threads can and TPL still can't do. One instance is creating a single threaded apartment (STA) thread, for very odd scenarios. Also, there are scenarios where using monitors, semaphores, AREs and other `System.Threading` classes may be slightly more performant due to level of control and less overhead involved. So to answer your question - TPL doesn't do everything, but those bits are so odd and few that you'll likely never need them. – bokibeg Sep 18 '21 at 08:44
  • 1
    Marked as duplicates: [Task vs Thread differences](https://stackoverflow.com/questions/13429129/task-vs-thread-differences), [What is the difference between task and thread?](https://stackoverflow.com/questions/4130194/what-is-the-difference-between-task-and-thread), [when to use Task and when to use Thread?](https://stackoverflow.com/questions/9727641/when-to-use-task-and-when-to-use-thread), [Task vs Thread differences](https://stackoverflow.com/questions/13429129/task-vs-thread-differences) – Theodor Zoulias Sep 18 '21 at 10:07
  • 1
    `Is there anything that threads can do` They allow you to _reliably_ spin up a new thread. Is that useful? Not commonly, no. – mjwills Sep 18 '21 at 10:53

2 Answers2

1

Tasks are nice for all the reasons you've mentioned, and they can reuse threads from a pool. This avoids the overhead of having lots of threads (each one needs a stack, and some control structures in the kernel, tracking them and so on) and also avoids the overhead of task switching - it takes some cycles for the kernel to transition between threads. If you have lots of threads competing for the same CPU then you'll spend more time switching and less time doing actual work.

As per one of the comments to your question using Threads directly means you can control the life cycle, the only other thing I can think of is Thread Local Storage (https://learn.microsoft.com/en-us/dotnet/standard/threading/thread-local-storage-thread-relative-static-fields-and-data-slots).

Daniel James Bryars
  • 4,429
  • 3
  • 39
  • 57
1

Yes, you need to learn about threads too. Here is a non-exhaustive list of things that you won't be able to do, if you know nothing about multithreading:

  1. You won't be able to synchronize the actions of your tasks, when these tasks are running in parallel to each other. By knowing nothing about locks, SemaphoreSlims, Mutexes, Barriers, Countdowns etc, your parallel and unsynchronized tasks are going to corrupt the non-thread-safe state of your application.
  2. You won't be able to do atomic mutations of variables and fields, that are used by your tasks, by utilizing the Interlocked class.
  3. You won't be able to prevent the compiler from reordering the instructions of your program, resulting to your tasks encountering invalid state, because you'll know nothing about memory barriers, the volatile keyword and the Volatile class.
  4. You won't be able to start a Task that runs on an STA thread.
  5. You won't be able to start a Task that runs on a foreground thread.
  6. You won't be able to start a Task that runs on a thread that has ThreadPriority other than Normal.
  7. You won't be able to utilize an efficient pool of objects, where each thread can use its own dedicated object (ThreadLocal<T>).

For learning multithreading, here is a free online resource: Threading in C# by Joseph Albahari.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • 1
    Thanks . I'm accepting this answer as sufficient because it actually answers the question as formulated -- although the things on this list are all pretty low-level use cases --- my intended scenarios (that i did NOT mention in the question) were about the line of business (LOB) apps --- as can be judged by comments below the question. Thanks again for the list and, especially the link to Albahari's work. – explorer Sep 19 '21 at 00:23
  • @explorer yeap, as long as you don't use tasks to introduce parallelism, and everything in your app runs sequentially, then learning about threads will be pretty much academic knowledge in your case. :-) – Theodor Zoulias Sep 19 '21 at 01:02