Mark Gravell gave a good answer: "What is a delegate."
Andrew Troelsen defines flow as
... the execution path inside the process. "Pro C # 2008 and the .NET 3.5 Platform," APress.
All processes running on your system have at least one thread. Let me call it the main thread. You can create additional streams for various reasons, but the most striking example to illustrate the purpose of streams is printing.
Say you open your favorite word processing application (WPA), type a few lines, and then want to print these lines. If your WPA uses the main stream to print a document, the WPA user interface will be frozen until printing is complete. This is because the main thread must print lines before it can handle any user interface events, i.e. Button clicks, mouse movements, etc. As if the code was written as follows:
do { ProcessUserInterfaceEvents(); PrintDocument(); } while (true);
Clearly, this is not what users want. Users want the user interface to respond while printing a document.
The answer, of course, is to print the lines in the second thread. Thus, the user interface can focus on handling user interface events, while the secondary stream focuses on printing lines.
The illusion is that both tasks are performed simultaneously. On a single processor machine, this may not be true, since the processor can only execute one thread at a time. However, switching between threads is so fast that the illusion is usually maintained. On a multiprocessor (or multipath) machine, this can be literally true, since the main thread can run on one processor, while the secondary thread runs on another processor.
In .NET, threads are a breeze. You can use the System.Threading.ThreadPool class, use asynchronous delegates, or create your own System.Threading.Thread objects.
If you are new to streaming, I would choose two warnings.
First, you can damage your application performance if you choose the wrong thread model. Be careful not to use too many threads or try to permeate everything that should happen sequentially.
Secondly (and more importantly), keep in mind that if you are exchanging data between threads, you will most likely need to synchronize access to this shared data, for example, using the lock keyword in C #. There is a lot of information on this topic available on the Internet, so I will not repeat it here. Just keep in mind that you may encounter intermittent, not always repetitive errors if you do not do this carefully.