C# provides rich multi-threaded operations, which brings great convenience to programming.
1. Reasons for using threads
1. You can use threads to isolate the code from other codes to improve the reliability of your application.
2. Threads can be used to simplify encoding.
3. Threads can be used to implement concurrent execution.
2. Basic knowledge
1. Process and thread: A process is the basic unit of the operating system executing program, and owns the application's resources. The process contains threads. The process's resources are shared by threads, and the thread does not own resources.
2. Foreground thread and background thread: The default is the foreground thread through the Thread class. When all foreground threads are closed, all background threads will be terminated directly and no exception will be thrown.
3. Suspend and Resume: Since the execution order of threads and the execution of the program are unpredictable, deadlocks are prone to occur when using suspend and wake up, and should be used as little as possible in actual applications.
4. Block thread: Join, block the calling thread until the thread terminates.
5. Terminate thread: Abort: Throw a ThreadAbortException exception and terminates the thread. The terminated thread cannot be awakened. Interrupt: Throw a ThreadInterruptException exception and let the thread terminate, and the execution can continue by catching the exception.
6. Thread priority: AboveNormal BelowNormal Highest Lowest Normal, default is Normal.
III. Use of threads
Thread functions are passed through delegately, either without parameters or with parameters (only one parameter), and can be encapsulated with a class or structure.
namespace Test { class Program { static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(TestMethod)); Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod)); = true; = true; (); ("hello"); (); } public static void TestMethod() { ("Thread function without parameters"); } public static void TestMethod(object data) { string datastr = data as string; ("Thread functions with parameters,The parameters are:{0}", datastr); } } }
4. Thread pool
Since the creation and destruction of threads requires a certain amount of overhead, excessive use of threads will cause waste of memory resources. For performance considerations, the concept of thread pool was introduced. The thread pool maintains a request queue. The thread pool's code extracts tasks from the queue and then delegates them to a thread in the thread pool for execution. The thread will not be destroyed immediately after execution. This can not only execute tasks in the background, but also reduce the overhead caused by thread creation and destruction.
The thread pool thread defaults to background threads (IsBackground).
namespace Test { class Program { static void Main(string[] args) { //Add work items to the thread pool queue, here you can pass a thread parameter (TestMethod, "Hello"); (); } public static void TestMethod(object data) { string datastr = data as string; (datastr); } } }
V. Task class
It is simple to initiate an asynchronous thread execution using the QueueUserWorkItem() method of ThreadPool, but the biggest problem with this method is that there is no built-in mechanism that allows you to know when the operation is completed, and whether there is a built-in mechanism that gets a return value after the operation is completed. To do this, the Task class in progress can be used.
Construct a Task<TResult> object and pass the return type of an operation to the generic TResult parameter.
namespace Test { class Program { static void Main(string[] args) { Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000); (); (); (); (); } private static Int32 Sum(Int32 n) { Int32 sum = 0; for (; n > 0; --n) checked{ sum += n;} //The result is too big, throwing an exception return sum; } } }
When a task is completed, a new task is automatically started.
After one task is completed, it can start another task. The previous code is rewritten below and does not block any threads.
namespace Test { class Program { static void Main(string[] args) { Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000); (); //(); Task cwt = (task => ("The result is {0}",)); (); } private static Int32 Sum(Int32 n) { Int32 sum = 0; for (; n > 0; --n) checked{ sum += n;} //The result overflows, throws an exception return sum; } } }
6. Delegate asynchronous execution
Asynchronous calls to delegate: BeginInvoke() and EndInvoke()
namespace Test { public delegate string MyDelegate(object data); class Program { static void Main(string[] args) { MyDelegate mydelegate = new MyDelegate(TestMethod); IAsyncResult result = ("Thread Param", TestCallback, "Callback Param"); //Asynchronous execution is completed string resultstr = (result); } //Thread function public static string TestMethod(object data) { string datastr = data as string; return datastr; } //Async callback function public static void TestCallback(IAsyncResult data) { (); } } }
7. Thread synchronization
1) Atomic operation (Interlocked): All methods perform an atomic read or a write operation.
2) lock() statement: Avoid locking the public type, otherwise the instance will exceed the scope of code control and define a private object to lock.
3) Monitor implements thread synchronization
The acquisition and release of exclusive locks is achieved through () and (). After obtaining, the resources are exclusive and other threads are not allowed to access.
There is also a TryEnter method, which will not block and wait when the resource is not requested. You can set the timeout time and return false if it is not retrieved.
4)ReaderWriterLock
When more reads and fewer writes are read, in order to improve the utilization rate of resources, let the read operation lock be a shared lock, multiple threads can read the resource concurrently, while the write operation is an exclusive lock, allowing only one thread to operate.
5) Event (Event) class implements synchronization
There are two states in the event class, the terminating state and the non-terminated state. Calling WaitOne during the terminating state can successfully request it, and the time state is set to the terminating state through Set.
①AutoResetEvent (autoReset event)
②ManualResetEvent (manual reset event)
6) Semaphore (Semaphore)
Semaphore is an int variable maintained by the kernel object. When it is 0, the thread is blocked, and when it is greater than 0, it is unblocked. When the waiting thread on a semaphore is unblocked, the semaphore counts +1.
The thread reduces the semaphore by 1 through WaitOne and increases the semaphore by 1 through Release, which is very easy to use.
7) Mutex (Mutex)
Exclusive resources, similar to Semaphore.
8) Synchronization between processes
System-level synchronization can be achieved by setting the name of the synchronization object. Different applications identify different synchronization objects through the name of the synchronization object.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.