That's it, period. Read again a few more times and remember!
What does that mean?
Well, the Task API proposes an abstract model that accommodates potentialdelayed-return execution. That simple. At its core, it doesn't impose anything on how the concrete implementation of that execution looks like.
Threads on the other hand, are a common, well-known, concrete mechanism that enable parallel code execution. They are baked into any modern operating system (Windows, Mac, Linux, Unix, Android, iOS, etc.). I'll not expand on them as there's tons of good, existing articles about threads (https://www.google.com/search?q=os+threads).
Here's a visual representation for easier understanding:
That being said, wrongfully thinking they're one and the same, comes from the fact that most practical use cases of Tasks are based on threads.
But that's not always the case and when it is we're actually just aligning thread programming syntax with the Task abstractions syntax.
Task.Run(() => {}) //will launch a new thread under the hood
The above examples have the same usage syntax, as they both return a Task instance, yet one of them spawns a new thread while the other one doesn't. For the consumer code... it's all the same.
With such an abstraction in place, .NET builds on top of it the well-known and blindly-used async/await sugar syntax, which, thanks to the Task abstract model, will compile into a state machine that massively simplifies the intensive effort of synchronizing delayed execution.
Expanding on this topic is a whole different article. For now it's enough to know that the Task abstraction enables the async/await syntax; which, needless to say, works regardless of the underlying implementation (with threads or without threads).
Therefore our own code becomes a lot cleaner, lighter and easier to read and maintain.
But again, remember:
Task == abstraction.
Thread == concrete mechanism.
Below, I wrote a couple of snippets that exemplify these concepts: