C # multithreading basics,
It took nearly two weeks to finish reading C # Essence. This book is very popular, but it is difficult to read the multi-thread and synchronization sections later, so I took notes, on the one hand, we should never forget. If the other method is incorrect, we are very glad that you have been enlightened by your predecessors.
What is a single thread? A single thread is known through a console Program
static void Main(string[] args){ var mainThread = Thread.CurrentThread;}
Add a breakpoint at Console. WriteLine to view main thread attributes
ApartmentSate: msdn generally means that threads in the same unit state can access each other. However, in. net, clr manages all shared resources in a thread-safe way.
CurrentCulture and CurrentUICulture indicate region information.
ExecutionContext: encapsulate context information related to threads
IsAlive: true if the thread has been started and has not been terminated or aborted. Otherwise, false
IsBackground: indicates whether it is a background thread
IsThreadPoolThread: indicates whether the thread is a thread in the thread pool.
ManagedThreadId: the unique identifier of the managed thread
For more information, see msdn.
Summary:
Thread definitions are available in many places. I would like to give an example. In many cases, we are a thread. We get up in the morning, have breakfast, go to work, and get off work ...... the ordered execution of these series of tasks is a single thread, but sometimes the second thread is actually started when reading novels while listening to songs. If you write code again at this time, the third thread is enabled.
Use Thread to create a Thread
Const int Repetitions = 100; static void Main (string [] args) {ThreadStart threadStart = DoWork; Thread thread = new Thread (threadStart); thread. start (); // The Main thread starts a loop for (int count = 0; count <Repetitions; count ++) {Console. write ('-');} Console. writeLine ("(the last statement of the main thread ...) ");} static void DoWork () {for (int count = 0; count <Repetitions; count ++) {Console. write ("+ ");}}
Run Ctrl + F5. You can see that the newly created thread and the loop in the Main thread are executed synchronously (several times more than once, so there will be different discoveries !)
So the question is, have we finished executing the created thread? When will the program end? Why is the created thread still output in the console after the last sentence of the main thread is completed?
Modify the program, press Ctrl + F5, and start the program several times. Different discoveries may occur!
Const int Repetitions = 100; static int index_thread = 0; static int index_main = 0; static void Main (string [] args) {ThreadStart threadStart = DoWork; thread thread = new Thread (threadStart); thread. start (); // The Main thread starts a loop for (int count = 0; count <Repetitions; count ++) {index_main ++; Console. write ('-');} Console. writeLine ($ "\ nindex_thread: {index_thread}"); Console. writeLine ($ "index_main: {index_main}"); Console. writeLine ("(the last statement of the main thread ...) ");} static void DoWork () {for (int count = 0; count <Repetitions; count ++) {index_thread ++; Console. write ("+"); if (count = Repetitions-1) {Debug. write ("the thread I created has been executed .............................. ....... \ n ");}}}
If you press F5 directly, you can see it in the output bar of Visual Studio.
Conclusion:
1. The operating system terminates the process after all foreground threads (the main thread and the newly created thread are both foreground threads). Although the index_thread output on the console is not always 100
2. The execution of threads other than the main thread is uncertain,
3. In fact, the main thread will end the main thread, close the process, and end the program after all the sub-threads (foreground threads) end.
4. Due to the uncertainty of the sub-main thread execution, when the main thread outputs index_thread, the sub-thread loop may end or not end, so the result is always not 100.
Use the Join method to block the main thread and wait until the execution of the sub-thread ends.
// Some code is omitted: thread. Join (); Console. WriteLine ($ "\ nindex_thread: {index_thread }");
In this way, we can ensure that after this, the sub-thread has finished running and the output result is 100 every time.
Use thread pool
Const int Repetitions = 1000; static int index_thread = 0; static int index_main = 0; static void Main (string [] args) {WaitCallback waitCallBack = DoWork; ThreadPool. queueUserWorkItem (waitCallBack, '+'); // The Main thread starts a loop for (int count = 0; count <Repetitions; count ++) {index_main ++; Console. write ('-');} Console. writeLine ($ "\ nindex_thread: {index_thread}"); Console. writeLine ($ "index_main: {index_main}"); Console. writeLine ("last statement of the main thread");} private static void DoWork (object ch) {for (int count = 0; count <Repetitions; count ++) {index_thread ++; Console. write (ch); if (count = Repetitions-1) {Debug. write ("the thread I created has been executed .............................. ....... \ n ");}}}
Advantages:
1. Solve the negative performance impact caused by too many threads
2. Efficient processor utilization
2. The code can be simplified by using the lambda delegate.
Notes
1. All threads created using the thread pool are background threads.
2. Do not use tasks with a particularly long thread pool running time. Do not restrict I/O as much as possible.
Asynchronous task
Static void Main (string [] args) {Task task = Task. run () => {var t = Thread. currentThread; for (int count = 0; count <Repetitions; count ++) {index_thread ++; Console. write ('+') ;}}); for (int count = 0; count <Repetitions; count ++) {index_main ++; Console. write ('-');} // similar to THread. join method task. wait (); Console. writeLine ($ "\ nindex_thread: {index_thread}"); Console. writeLine ($ "index_main: {index_main}"); Console. writeLine ("Over"); Console. readLine ();}
Task is a class library introduced by. Net Framwwork4. It is easier to use than Thread, and has better controllability and THreadPool. By default, Task is also requested by a Thread from the Thread pool to execute the Task.
The same as ThreadPool, when it is created (call Run) to start, and the same as Thread, you can use the Wait () method to block the context Thread (main Thread) and Wait for the task to be completed.
Asynchronous tasks with return values
Static void Main (string [] args) {Task <string> task = Task. run () => "string type return value"); for (int I = 0; I <1000; I ++) {if (task. isCompleted) {Console. write ('Task completed '); break;} Console. write ('. ');} Console. writeLine (task. result );}
A generic Task indicates that the Task has a returned value, and IsCompleted indicates whether the Task is completed.
It should be noted that when the Result attribute is called, the context process will be blocked (Wait () is executed internally ())