Solution to multiple initialization problems in multiple threads
Today, when I read the source code of the MSDN library, I found a class
LazyInitializer.EnsureInitialized
Used in parallel computing.
MSdn code
// Used to hold any exceptions encountered during action processing ConcurrentQueue
exceptionQ = null; // will be lazily initialized if necessary // This is more efficient for a large number of actions, or for enforcing MaxDegreeOfParallelism. try { // Launch a self-replicating task to handle the execution of all actions. // The use of a self-replicating task allows us to use as many cores // as are available, and no more. The exception to this rule is // that, in the case of a blocked action, the ThreadPool may inject // extra threads, which means extra tasks can run. int actionIndex = 0; ParallelForReplicatingTask rootTask = new ParallelForReplicatingTask(parallelOptions, delegate { // Each for-task will pull an action at a time from the list int myIndex = Interlocked.Increment(ref actionIndex); // = index to use + 1 while (myIndex <= actionsCopy.Length) { // Catch and store any exceptions. If we don't catch them, the self-replicating // task will exit, and that may cause other SR-tasks to exit. // And (absent cancellation) we want all actions to execute. try { actionsCopy[myIndex - 1](); } catch (Exception e) { LazyInitializer.EnsureInitialized
>(ref exceptionQ, () => { return new ConcurrentQueue
(); }); exceptionQ.Enqueue(e); } // Check for cancellation. If it is encountered, then exit the delegate. if (parallelOptions.CancellationToken.IsCancellationRequested) throw new OperationCanceledException(parallelOptions.CancellationToken); // You're still in the game. Grab your next action index. myIndex = Interlocked.Increment(ref actionIndex); } }, TaskCreationOptions.None, InternalTaskOptions.SelfReplicating); rootTask.RunSynchronously(parallelOptions.EffectiveTaskScheduler); rootTask.Wait(); } catch (Exception e) { LazyInitializer.EnsureInitialized
>(ref exceptionQ, () => { return new ConcurrentQueue
(); }); // Since we're consuming all action exceptions, there are very few reasons that // we would see an exception here. Two that come to mind: // (1) An OCE thrown by one or more actions (AggregateException thrown) // (2) An exception thrown from the ParallelForReplicatingTask constructor // (regular exception thrown). // We'll need to cover them both. AggregateException ae = e as AggregateException; if (ae != null) { // Strip off outer container of an AggregateException, because downstream // logic needs OCEs to be at the top level. foreach (Exception exc in ae.InnerExceptions) exceptionQ.Enqueue(exc); } else { exceptionQ.Enqueue(e); } } // If we have encountered any exceptions, then throw. if ((exceptionQ != null) && (exceptionQ.Count > 0)) { ThrowIfReducableToSingleOCE(exceptionQ, parallelOptions.CancellationToken); throw new AggregateException(exceptionQ); }
The following content is reprinted to help you better understand the content above.
LazyInitializer. ensureInitialized is a new feature introduced by frameworks4.0. It implements the attribute delay initialization function and is used in System. in the Threading namespace, it is closely related to multithreading, that is, when many people use this method synchronously, it plays a role in the stored objects. This is related to msdn:
This method can be used by multiple executors to initialize the Target directory object.
When multiple executors access this method at the same time, multiple T execution individuals may be created, but only one execution individual will be stored to the target. In some cases, this method will not place unstored objects. If this type of object must be placed, the call end determines the failed object and places the object properly.
Synchronization with unclear concepts does not matter. See the example below for full understanding.
The following example describes the two initialization attributes and compares them. The advantage of delayed Initialization is that the object is initialized when it is used. In a method body, if an object is initialized once, do not perform repeated initialization.
Code 1 demonstrates poor performance
Code 2, showing the Optimization
Code 3: Microsoft encapsulates the program for us and performs better in multiple threads.
Code 1:
class TestLazyInitializer1 { public People People { get { return new People(); } } public void Print1() { Console.WriteLine(People.Name); } public void Print2() { Console.WriteLine(People.Name); } }
Code 2:
class TestLazyInitializer2 { People _people; public People People { get { return _people == null ? (_people = new People()) : _people; } } public void Print1() { Console.WriteLine(People.Name); } public void Print2() { Console.WriteLine(People.Name); } }
Code 3
Class TestLazyInitializer {private People _ people ;////// Specify attributes for delayed initialization ///Public People {get {return LazyInitializer. ensureInitialized (ref _ people, () =>{ return new People () ;}) ;}} public void Print1 () {Console. writeLine (People. name);} public void Print2 () {Console. writeLine (People. name );}}
However, we can imagine the results of their running. When a class uses the same object multiple times, the performance is poor. The returned Name (current time) must be different, the returned Name (current time) must be a value if the performance is good and only initialized once.