C # concurrent use real Parallel.ForEach

     Introduction: Recently meals to the customer to develop a computing system, the calculation takes about 2000 individual meals. Consumer demand is recorded in accordance with a predetermined Daily meal plan staff check if the card is not reported to have meals or meal did not have a report card to a certain set of rules such as the amount of deductions. At first I thought is simple, direct use a for loop to get statistical results I was not the problem, but it is too slow to calculate the need for 7-8 minutes. Such a system service timeout error is reported, people feel a little less cool. Because time is not much it will be submitted to users, and added logic behind the calculation time becomes longer, the entire calculation again to actually nearly 10 minutes. The user is able to receive (I realize I need to hand count a few days of it), but I can not accept, so he began to optimize, and how to optimize it, multithreading chant.

     Mention of multi-threading, the first thought is Task, after all, more than .net4.0 Task encapsulates many useful ways. But after all, it is to open Task some threads to perform the task, the final result of the integration, which can be faster, but I think some of the more rapid, so the thought of another object: Parallel. Prior to maintain the code is actually written by someone else has encountered Parallel.Invoke, just specify the function of the role is to perform multiple tasks concurrently, if the operation more time-consuming experience, they do not contribute between variables of this method is good . My situation is to be executed concurrently a collection, so he used the List.ForAll this method is actually a method to expand and complete the call is:. List.AsParallel () ForAll, need to be converted into a set of concurrent, equivalent to Parallel. ForEach, object of a series of operations is performed concurrently on the inside of the collection element.

     Ever since, the original foreach replaced List.AsParallel (). ForAll, up and running, and sure enough at an alarming rate, less than two minutes to insert the results, but in the end it is reported that the main key to repeat the mistake, this is the wrong reasons, due to the use of concurrent, this time variable increment is in fact a strong increment, when multiple threads at the same time to get the id value, then go increment repeated, for example as follows:

            int num = 1;
            List<int> list = new List<int>();
            for (int i = 1; i <= 2000; i++)
            {
                list.Add(i);
            }
            Console.WriteLine($"num初始值为:" + num.ToString());
            list.AsParallel().ForAll(n =>
            {
                num++;
            });
            Console.WriteLine($" Unlock, after concurrent {list.Count} times as: " + num.ToString ()); 
            the Console.ReadKey ();

This code is to allow a variable execution times since 2000 by the normal result should be 2001, but the actual results are as follows:

Experienced students can immediately think of the need to lock, C # built a lot of lock objects, such as lock mutex, Interlocked internal lock, Monitor these more common, in fact, achieve internal lock using a Monitor object. Variable increment, the Interlocked object provides a variable increment, decrement, addition, or the like, we use the method Interlocked.Increment increment, the function is defined as: int Increment (ref int num) , the object is to provide atomicity variable increment operation, the incoming target value, or returns ref num are the result of self-energizing. On the basis of our previous increase some code:

           = NUM . 1 ; 
            Console.WriteLine ($ " NUM initial value: " + num.ToString ()); 
            . list.AsParallel () The ForAll (n- => 
            { 
                Interlocked.Increment ( REF NUM); 
            }); 
            Console.WriteLine ($ " use internal lock, the concurrent {list.Count} times as: " + num.ToString ()); 
            the Console.ReadKey ();

Let's look at the results:

ID plus a lock after repeated be solved, in fact, not happy too early, due to normal environment with ID ID we still use these objects to build it, so he wrote to write code, using a collection to add these ID, for more realistic simulation production environment, I forAll inside with another layer of loop code as follows:

            num = 1;
            Random random = new Random();
            var total = 0;
            var m = new ConcurrentBag<int>();
            list.AsParallel().ForAll(n =>
            {
                var c = random.Next(1, 50);
                Interlocked.Add(ref total, c);
                for (int i = 0; i < c; i++)
                {
                    Interlocked.Increment ( REF NUM); 
                    m.Add (NUM); 
                } 
            }); 
            Console.WriteLine ($ " use internal lock, concurrent + {list.Count} After the inner loop times as: " + num.ToString ()) ; 
            Console.WriteLine ($ " The actual value: Total +. 1 {} " );
             var L = m.GroupBy (n-=> n-) .Where (= O> o.Count ()> . 1 ); 
            Console.WriteLine ( $ " concurrent inside the safe collection ConcurrentBag added num, set duplicates: {l.Count ()} th " ); 
            the Console.ReadKey ();

I used the above code inside the thread-safe collection ConcurrentBag <T> is its namespace: using System.Collections.Concurrent, despite the use of thread-safe collection, but in the face of concurrent still unsafe, in fact, here to compare depressed , increase self-locking, internal security should also use a set of locks, but still repeated. Justified, and think there is a multi-threaded execution context object, that is, when multiple threads to perform tasks simultaneously, they share a variable object began to pass into the value should be the same, due to the addition of the variable self-locking when increased, so ID is not repeated. I guess the problem should be out in the Add method, and that is when the num value increment have not had time to spread opinions Add method has been executed, so add a duplicate variables. Ever since, I re-wrote a piece of code, let ID increment and collections are added into the lock inside:

            num = 1;
            total = 0;
            using (var q = new BlockingCollection<int>())
            {
                list.AsParallel().ForAll(n =>
                {
                    var c = random.Next(1, 50);
                    Interlocked.Add(ref total, c);
                    for (int i = 0; i < c; i++)
                    {
                        
                       // Task.Delay(100);
                        q.Add (Interlocked.Increment (REF NUM)); 
                        
                        // controllable
                         // Lock (objLock)
                         // {
                         //     NUM ++;
                         //     q.Add (NUM);
                         // } 
                    } 

                }); 
                q.CompleteAdding ( ); 
                Console.WriteLine ($ " NUM integrated value: {total}, then concurrent values: {NUM} " );
                 var X = q.GroupBy (n-=> n-) .Where (= O> o.Count ( )> 1 ); 
                Console.WriteLine ($ "Concurrent use of safety set BlockingCollection + Interlocked added num, set duplicates: {x.Count ()} th " ); 
                the Console.ReadKey (); 
            }

Here I tested another set of thread-safe BlockingCollection, use about this collection, please find the MSDN documentation on their own, directly above the key code value added return safe collection, you can ensure the collection will not be repeated, but in fact below the lock using a formal environment because usually the object of our added value will not be the type of foundation, operating results are as follows:

So far, we have solved the problem.

      Summary: C # collection of security in the case of concurrent fact, is not necessarily safe, still need to verify the actual application scenarios and the results shall prevail. Parallel.ForEach in the number of cycles impressive is that you can use to go, if there is a shared variable, be sure to make syncing with the lock. This approach still have to be used with caution, if there is an internal method to access the database remember to increase the transaction, otherwise the Ha ha.

Guess you like

Origin www.cnblogs.com/heweijian/p/11330282.html