[C#] Parallel programming practice: realizing data parallelism (3)

        This chapter continues to learn about implementing data parallelism. This article mainly introduces cancellation loops.

        This tutorial corresponds to Learning Engineering: Magician Dix / HandsOnParallelProgramming · GitCode        


4. Cancel the cycle

        In sequential loops, break can be used to break out of the loop, while in case of parallel loops, break and continue keywords cannot be used since they are executed on multiple threads.

4.1、ParallelLoopState.Break

        The test code is as follows, here we set the maximum parallelism to 2:

        private void CancelByParallelBreak()
        {
            int length = commonPanel.GetInt32Parameter();
            var L = TestFunction.GetTestList(length);            
            //设置最大并行度为2
            var options = new ParallelOptions { MaxDegreeOfParallelism = 2 };
            var result = Parallel.ForEach(L, options, (i, state) =>
            {
                Debug.Log(L[i]);
                state.Break();//跳出循环
                Debug.Log($"Run Parallel Break At {Task.CurrentId}");
            });
            Debug.Log($"CancelByParallelBreak End!  : {result.IsCompleted} | {result.LowestBreakIteration}");
        }

        At this point, we see the print results as follows:

         The two threads are interrupted after each printing once, and the returned IsCompleted is False.

        When any processor encounters the Break() method, it will set an iteration number in the LowestBreakIteration property of the ParallelLoopState object, which will be the maximum number of iterations or the last iteration that can be executed. All other tasks will continue to iterate until this number is reached.

        Therefore, we partially modify the code:

……
Debug.Log($"Log {L[i]} At :{Task.CurrentId}");
if (L[i] == 0)
{
    state.Break();
    Debug.Log($"Run Parallel Break At {Task.CurrentId}");
}
……

        Here we set the value to 0 in one of the threads, that is, we break the loop in the first loop. But the resulting printout is different:

         It can be seen that another thread executes 3 times before breaking the loop.

4.2、ParallelLoopState.Stop

        The same method as above, also set the parallelism to 2, we change Break to Stop:

        private void CancelByParallelStop()
        {
            int length = commonPanel.GetInt32Parameter();
            var L = TestFunction.GetTestList(length);
            var options = new ParallelOptions { MaxDegreeOfParallelism = 2 };
            var result = Parallel.ForEach(L, options, (i, state) =>
            {
                Debug.Log($"Log {L[i]} At :{Task.CurrentId}");

                if (L[i] == 0)
                {
                    state.Stop();
                    Debug.Log($"Run Parallel Stop At {Task.CurrentId}");
                }
            });
            Debug.Log($"CancelByParallelStop End!  : {result.IsCompleted} | {result.LowestBreakIteration}");
        }

        The printed results are as follows:

 

4.3、CancellationToken

        Like normal tasks, iterations can also be canceled using the CacellationToken class. When the token is canceled, the loop will complete the iteration currently running in parallel, but will not start a new iteration. The parallel loop will throw an OperationCanceledException when existing iterations complete. The code example is as follows:

        private void CancelByParallelCancellationToken()
        {
            int length = commonPanel.GetInt32Parameter();
            var L = TestFunction.GetTestList(length);

            //将取消令牌设置为运行参数
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            var options = new ParallelOptions
            {
                MaxDegreeOfParallelism = 2,
                CancellationToken = cancellationTokenSource.Token
            };

            //开始运行循环
            Debug.Log($"Start Run Parallel.ForEach : {length}");
            Task.Run(() =>
            {
                var result = Parallel.ForEach(L, options, (i, state) =>
                 { 
                     //等待 100 ms
                     Thread.Sleep(100);                     
                     Debug.Log($"Log {L[i]} In Task :{Task.CurrentId}");                    
                 });
                Debug.Log($"Parallel.ForEach End  : {result.IsCompleted} | {result.LowestBreakIteration}");
            });

            //等待250 ms 后取消循环
            Task.Run(async () =>
            {
                await Task.Delay(250);
                cancellationTokenSource.Cancel();
                Debug.Log("Cancel Token !");
            });
        }

        Likewise, the degree of parallelism is 2. But this time the code has been modified: the previous code will actually be synchronized on the main thread, this time we put it in the Task to execute the cyclic task, and wait for 100ms every time a value is printed. Then cancel the token is also executed in the Task, wait for 250ms and then cancel.

        Reasoning directly from the code, the above loop has already canceled the task during the Sleep process when it prints for the third time (starting at 200ms and ending at 300ms). Since the scheduled tasks will still be executed, the third print will take effect, but the fourth print will not:

         It can be seen that the printing results are consistent with expectations.

        But it is worth noting: Parallel.ForEach End this line of printing (24 lines) did not print out! That is, the cancellation token directly cancels the entire thread, not just cancels the loop !


        To be continued.

        This tutorial corresponds to Learning Engineering: Magician Dix / HandsOnParallelProgramming · GitCode        

Guess you like

Origin blog.csdn.net/cyf649669121/article/details/131516834