In a multithreaded program, if a shared resource is used by multiple threads at the same time, it can cause a multi-threaded problem, depending on whether an operation on that resource is thread-safe. For example. NET dictionary is a completely thread insecure data structure, for dictionary insert, delete can lead to multi-threading problems, mainly because dictionary internal implementation structure will be frequently due to insert, delete operation and change the length, at this time, In the event of multithreading problems, the program is most likely to throw an array out of bounds exception. In particular, for WebService, each request generates a thread/instance, so pay special attention to multithreading issues.
In general, multithreading issues often occur in conjunction with shared resources. For example, for the use of member variables of a class, for the use of global static variables, and for local variables inside a function, there is no multithreading problem for the generic, because each thread generates a copy of the function's internal member variables when invoking a particular function, and the threads and threads are mutually irrelevant.
The most common way to solve a multithreaded problem is to lock it so that a resource can only be used by one thread at a time, while other threads must wait outside the locked code until the lock is lifted, as shown in the following C # code:
1 Lock (_lock) 2 {3 //do something to theshared resources. 4 }
Let's talk about double-check.
Multithreading problems are often associated with a lazy-initialize design pattern. Here it will slowly lead to double-check. Lazy-initialize, for some particularly complex objects, let the program initialize it the first time it is called, and it is guaranteed to be initialized only once.
The first design to think of is this:
1 classA2 {3 4 5 PrivateComplexclass _result =NULL;6 7 Publiccomplexclass GetResult ()8 {9 if(_result = =NULL)Ten { One_result =NewComplexclass (); A } - return_result; - } the}
But there's a problem with that. When the Complexclass is constructed longer, _result may be null when the first thread is still in the Complexclass construct, or it may point to an object that has not yet been initialized. This way, either two threads initialize the Complexclass two times, or the second thread returns a reference to an incomplete object. Therefore, a lock is required here, as shown below:
1 complexclass GetResult ()2 {3 Lock(_lock)4 {5 if(_result = =NULL)6 {7_result =NewComplexclass ();8 }9 }Ten return_result; One}
This way, although a multi-threaded problem is resolved, a lock is requested every time a result is required, and the request lock has a significant impact on the performance of the program, so we add a check to the outside of Lock:
1 complexclass GetResult ()2 {3 if(_result = =NULL)4 {5 Lock(_lock)6 {7 if(_result = =NULL)8 {9_result =NewComplexclass ();Ten } One } A } - return_result; -}
In this way, for all requests after initialization, no lock is requested, but the _result is returned directly.
But there is still a little problem. For some programming languages, _result = new Complexclass (); This code causes _result to point to a partially initialized object. That is, when thread a initializes the Complexclass, thread B may be able to determine that _result is not NULL, but when initialization is not yet complete, then thread B directly returns a partially initialized object, causing the program to crash. So, how do we solve this problem? The general solution is to add a buffer inside the program with a local variable (identity variable):
1 complexclass GetResult ()2 {3 complexclass result;4 if(_result = =NULL)5 {6 Lock(_lock)7 {8 if(_result = =NULL)9 {Tenresult =NewComplexclass (); One_result =result; A } - } - } the return_result; -}
In this way, the above problem is completely solved ~
Multithreading problem and double-check