C # Study Notes 14,
1.Avoid using this, typeof (type), and string to synchronize data from multiple threads, which may lead to deadlocks.
2.Interlocked: the mutex Lock mode (Data Synchronization) We generally use is the Lock keyword (that is, the Monitor class). This synchronization is a very costly operation. In addition to using Monitor, there is also an alternative, which is usually directly supported by the processor and oriented to specific synchronization modes. The Interlocked class contains some common methods, such as CompareExchange, Decrement, Increment, and Exchange. These data are synchronized to a single value (object) for data processing.
3.Event Notification when multiple threads exist: You can view the Utility. EventInThread () code list.
4.Best practices for synchronous design:
(1) avoid deadlocks. For example, if both threads wait for the resources locked by the other party to be released, thread A locks sync1 resources, thread B locks sync2 resources, and thread A requests to lock sync2 resources, when thread B requests to lock sync1 resources, a deadlock occurs. The occurrence of a deadlock must meet four basic conditions. Mutual Exclusion, possession and waiting, cannot be preemptive, and cyclic waiting conditions.
(2) When to provide synchronization. static data is usually synchronized and there are public methods to modify the data. The internal method should handle the synchronization problem.
(3) Avoid unnecessary locks.
5.More synchronization types: the System. Threading. Mutex class is conceptually consistent with the Monitor class, except that it supports synchronization between processes. If you synchronize access to files or other cross-Process resources, you can only run one instance. For example, Utility. UseMutex () code list. The Mutex class is derived from WaitHandle and can automatically obtain multiple locks (not supported by the Monitor class ).
6.WaitHandle class: Multiple synchronization classes inherit from it, such as Mutex, EventWaitHandle, Semaphore. The key method of the WaitHandle class is WaitOne (), which has multiple overloaded versions, these methods will block the current thread until the WaitHandle instance receives a signal or is Set (call Set ()).
7.Reset event class: the reset event has nothing to do with the delegate and event in C #. It is used for multi-thread control and the reset event is used to force the code to wait for the execution of another thread, wait until you get the notification that the event has occurred. The reset event classes include ManualResetEvent and ManualResetEventSlim (. net4.0 is added and optimized for the former), AutoResetEvent (mainly using the first two types), and the key methods they provide are Set () and Wait (). Calling the Wait () method will block the execution of a thread until a different thread calls Set () or the Set Wait time ends. You can view the Utility. UseManualResetEvent () code list.
8.Concurrent collection class:. net4.0 adds some classes that are concurrent collection classes. These classes are specially designed to include built-in synchronization code so that they can support access by multiple threads without worrying about competing conditions. For example, BlockingCollection <T>, ConcurrentBag <T>, ConcurrentDictionary <K, V>, ConcurrentQueue <T>, and ConcurrentStack <T>, A common mode that can be implemented is thread-safe access by producers and consumers.
9.Local thread storage: an alternative solution for synchronization is isolation. One way to achieve isolation is to use local thread storage and use local thread storage, and the thread has an exclusive variable instance. There are two methods for local thread storage implementation: ThreadLocal <T> and ThreadStaticAttribute. The ThreadLocal <T> class is added to. net4.0. You can view the code list of the LocalVarThread class.
10.Timer: There are three Timers: System. windows. forms. timer, System. timers. timer, System. threading. timer, Forms. timer is used for user interface programming. It can securely access forms and controls on the user interface, Timers. timer is Threading. the Timer package is an abstraction of its functions (System. threading. timer type is lightweight ).
Function Description |
System. Timers. Timer |
System. Threading. Timer |
System. Window. Forms. Timer |
Allows you to add and delete listeners after timer instantiation. |
Yes |
No |
Yes |
Supports callback on the user interface thread |
Yes |
No |
Yes |
Callback from the thread obtained from the self-Thread Pool |
Yes |
Yes |
No |
Supports drag and drop in Windows Form Designer |
Yes |
No |
Yes |
Suitable for running in a multi-threaded server environment |
Yes |
Yes |
No |
Supports passing any State from timer initialization to callback |
No |
Yes |
No |
Implement Idisposable |
Yes |
Yes |
Yes |
Support switch-on and periodic repeated callbacks |
Yes |
Yes |
Yes |
Cross-application domain access |
Yes |
Yes |
Yes |
Supports Icomponent, which can be contained in an Icontainer |
Yes |
No |
Yes |
11.Async Program Model (APM): asynchronous programming is a multi-thread method, the key to APM is to use the BeginX and EndX methods in pairs (X usually corresponds to the synchronous version of the method name), and these methods have a complete signature. Begult returns a System. IAsyncResult object that can be used to access the asynchronous call status, waiting for completion or polling. However, the EndX method obtains the returned object as the input parameter. In this way, we can match the two methods into a pair, so that we can clearly determine which BeginX method call matches the EndX method call. The essence of APM requires that all BeginX calls must have one (and only one) EndX call. Therefore, it is impossible for two EndX calls to accept the same IAsyncResult instance. We can also use the WaitHandle of IAsyncResult to determine when the Asynchronous Method ends. The WaitHandle of IAsyncResult is notified before the callback is executed.
The EndX method has four purposes.
First, calling EndX will block the thread from continuing to execute until the request is successfully completed (or an error occurs and an exception is thrown ).
Second, if method X returns data, this data can be accessed from the EndX method call.
Again, if an exception occurs during request execution, you can re-raise this exception when calling EndX to ensure that the exception will be discovered by the calling code-as if it occurred on a synchronous call.
Finally, if any resource needs to be cleared after calling X, EndX will be responsible for clearing these resources.
The BeginX method has two additional parameters, which are not available in the synchronous version method. One is the callback parameter and the other is the System to be called at the end of the method. asyncCallback delegate, and the other is the object-type State parameter (State ). When using callback, you can place EndX inside it for execution.
12.Call APM using TPL (task parallel Library): Although TPL greatly simplifies the asynchronous call of long-running methods, it is usually best to use the APM method provided by the API instead of compiling TPL for the synchronous version. This is because API developers know how to write the most efficient thread processing code and what data to synchronize and what synchronization types to use. TPL contains a set of overloaded versions of FromAsync for calling APM.
13.Asynchronous delegate call: There is a derived APM mode called asynchronous delegate call. It uses special code generated by the C # compiler for all delegate data types. For example, given a delegate instance of Func <string, int>, you can use the following APM method pairs on this instance.
System. IAsyncResult BeginInvoke (string arg, AsyncCallback callback, object obj)
Int EndInvode (IasyncResult result)
The result is that you can use the method generated by the C # compiler to synchronously call any delegate (and then call any method ). Unfortunately, the basic technology used in the asynchronous delegated call mode is a distributed programming technology that is no longer developed. It is called remote processing. Although Microsoft still supports asynchronous delegate calls and does not give up its support for it in the foreseeable future, its performance is more general than other technologies. Other technologies include Thread, ThreadPool, and TPL. Therefore, when developing a new project, developers should try to use other technologies instead of using asynchronous delegation to call APIs. Before TPL, the asynchronous delegated call mode is much easier than other alternatives. Therefore, if an API does not provide an explicit asynchronous call mode, it is generally used. However, after the advent of TPL, asynchronous delegate calls become useless unless it is intended to be compatible with. Net3.5 and earlier frameworks.
14.Event-based asynchronous mode (EAP): a more advanced programming mode than APM is event-based asynchronous mode. Like APM, API developers implement EAP for long-running methods. The Background Worker mode is a specific implementation of EAP.
15.Background Worker mode: the process of establishing the Background Worker mode is as follows.
(1) register a long-running method for the BackgroundWorker. DoWork event.
(2) To receive progress or status notifications, hook a listener for BackgroundWorker. ProgressChanged and set BackgroundWorker. WorkerReportsProgress to true.
(3) register a method for the BackgroundWorker. RunWorkerCompleted event.
(4) assign a value to the workersuppscanscancellation attribute to cancel an operation. After the value of true is assigned to this attribute, the DoWorkEventArgs. CancellationPending flag is set for calls to BackgroundWorker. CancelAsync.
(5) Check the DoWorkEventArgs. CancellationPending attribute value in the method provided by DoWork, and exit the method when it is true.
(6) After everything is set, call BackgroundWorker. RunWorkerAsync () and provide a status parameter to be passed to the specified DoWork () method to start work.
After being divided into the preceding steps, the Background Worker mode is easy to understand. In addition, because it is essentially an EAP, it provides explicit support for progress notifications. When the background worker (worker) thread executes asynchronously, if an unhandled Exception occurs, the RunWorkerCompletedEventArgs. Error attribute entrusted by RunWorkerCompleted is set to an Exception instance. Therefore, we provide an exception handling mechanism by checking the Error attribute in the RunWorkerCompleted callback.
16.Windows UI programming: when using System. Windows. Forms and System. Windows namespaces for user interface development, you must also pay attention to thread processing issues. Microsoft Windows operating systems use a single-threaded, message-based user interface. This means that only one thread can access the user interface at a time. Any interaction with the rotation thread should be sent through the Windows message pump.
17.Windows Forms: when programming Windows Forms, to check whether a UI call is allowed from a thread, you need to call the InvokeRequired attribute of a component to determine whether the encapsulation is required. If InvokeRequired returns true, it indicates that it needs to be sent and can be implemented through an Invoke () call. Although InvokeRequired is checked internally by Invoke (), it is more efficient to check this attribute explicitly in advance. Since sending data to another thread may be a very slow process, you can use BeginInvoke () and EndInvoke () for asynchronous calling. Invoke (), BeginInvoke (), EndInvoke (), and InvokeRequired constitute members of the System. ComponentModel. ISynchronizeInvoke interface. This interface has been implemented by System. Windows. Forms. Control. All Windows Form Controls are derived from this Control class.
18.Windows Presentation Foundation (WPF): to implement the same sending and receiving check on the WPF platform, a slightly different method is required. WPF contains a static member attribute named Current. Its type is DispatcherObject, which is provided by the System. Windows. Application class. Calling CheckAccess () on the scheduler (dispatcher) object is equivalent to calling InvokeRequired on controls in Windows Forms, and then using Application. current. dispatcher. invoke () method.
19.Note: In addition to the model provided by TPL, there are so many additional models available, which makes many people do not know how to choose. In general, it is best to select the mode provided by the API (such as APM or EAP), and finally select the TPL mode.
Public class Utility {private static ManualResetEventSlim firstEvent, secendEvent; private static object _ data; public static void Initialize (object newValue) {Interlocked. compareExchange (ref _ data, newValue, null);} public static void EventInThread () {// It is not thread-safe. Between checking the delegate object and calling the delegate, other threads assign values to OnTemparatureChange, which may be set to null. /* If (OnTemparatureChange! = Null) {// call the subscriber OnTemparatureChange (this, new TemparatureEventArgs ();} * // thread-safe operation, create a copy of the delegate variable, and check the null copy, trigger the copy. In this way, even if the OnTemparatureChange delegate variable is null in other threads, it will not be affected. /* TemparatureChangedHandle localChanged = OnTemparatureChange; if (localChanged! = Null) {// call the subscriber localChanged (this, new TemparatureEventArgs ();} */} public static void UseMutex () {bool firstApplicationInstance; string mutexName = Assembly. getEntryAssembly (). fullName; using (Mutex mutex = new Mutex (false, mutexName, out firstApplicationInstance) {if (! FirstApplicationInstance) {Console. writeLine ("This Application is already running. ");} else {Console. writeLine ("Enter to shutdown. "); Console. readLine () ;}} public static void UseManualResetEvent () {using (firstEvent = new ManualResetEventSlim () using (secendEvent = new ManualResetEventSlim () {Console. writeLine ("App start"); Console. writeLine ("start task"); Task task Task = task. factory. startNew () => {Console. writeLine ("DoWork start"); Thread. sleep (1000); firstEvent. set (); secendEvent. wait (); Console. writeLine ("DoWork end") ;}); firstEvent. wait (); Console. writeLine ("Thread executing"); secendEvent. set (); task. wait (); Console. writeLine ("Thread completed"); Console. writeLine ("App shutdown") ;}} public class LocalVarThread {public static ThreadLocal <double> _ count = new ThreadLocal <double> () => 0.01134 ); public static double Count {set {_ count. value = value;} get {return _ count. value ;}} public static void DoWork () {Task. factory. startNew (Decrement); for (int I = 0; I <short. maxValue; I ++) {Count ++;} Console. writeLine ("DoWork Count = {0}", Count);} public static void Decrement () {Count =-Count; for (int I = 0; I <short. maxValue; I ++) {Count --;} Console. writeLine ("Decrement Count = {0}", Count );}}View Code
--------------------- The above content is organized according to "C # the third edition of this topic ".