From 2d2fb4b366a3e641a0e0dc72f36387c88bdafe25 Mon Sep 17 00:00:00 2001 From: "gu.martinm@gmail.com" Date: Sun, 18 May 2014 03:49:11 +0200 Subject: [PATCH] Threads and C# http://parallelpatterns.codeplex.com/ Chapter 2: Parallel Loops --- Allgemeines/Threads/Threads.sln | 149 ++++++++++ Allgemeines/Threads/Threads/Chapter2.cs | 319 +++++++++++++++++++++ Allgemeines/Threads/Threads/Program.cs | 17 ++ .../Threads/Threads/Properties/AssemblyInfo.cs | 22 ++ Allgemeines/Threads/Threads/Threads.csproj | 45 +++ 5 files changed, 552 insertions(+) create mode 100644 Allgemeines/Threads/Threads.sln create mode 100644 Allgemeines/Threads/Threads/Chapter2.cs create mode 100644 Allgemeines/Threads/Threads/Program.cs create mode 100644 Allgemeines/Threads/Threads/Properties/AssemblyInfo.cs create mode 100644 Allgemeines/Threads/Threads/Threads.csproj diff --git a/Allgemeines/Threads/Threads.sln b/Allgemeines/Threads/Threads.sln new file mode 100644 index 0000000..cc1d37d --- /dev/null +++ b/Allgemeines/Threads/Threads.sln @@ -0,0 +1,149 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Threads", "Threads\Threads.csproj", "{CED02136-FA8A-4E92-9BC6-3340DA89C3A4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CED02136-FA8A-4E92-9BC6-3340DA89C3A4}.Debug|x86.ActiveCfg = Debug|x86 + {CED02136-FA8A-4E92-9BC6-3340DA89C3A4}.Debug|x86.Build.0 = Debug|x86 + {CED02136-FA8A-4E92-9BC6-3340DA89C3A4}.Release|x86.ActiveCfg = Release|x86 + {CED02136-FA8A-4E92-9BC6-3340DA89C3A4}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Threads\Threads.csproj + Policies = $0 + $0.DotNetNamingPolicy = $1 + $1.DirectoryNamespaceAssociation = None + $1.ResourceNamePolicy = FileFormatDefault + $0.NameConventionPolicy = $2 + $2.Rules = $3 + $3.NamingRule = $4 + $4.Name = Namespaces + $4.AffectedEntity = Namespace + $4.VisibilityMask = VisibilityMask + $4.NamingStyle = PascalCase + $4.IncludeInstanceMembers = True + $4.IncludeStaticEntities = True + $3.NamingRule = $5 + $5.Name = Types + $5.AffectedEntity = Class, Struct, Enum, Delegate + $5.VisibilityMask = Public + $5.NamingStyle = PascalCase + $5.IncludeInstanceMembers = True + $5.IncludeStaticEntities = True + $3.NamingRule = $6 + $6.Name = Interfaces + $6.RequiredPrefixes = $7 + $7.String = I + $6.AffectedEntity = Interface + $6.VisibilityMask = Public + $6.NamingStyle = PascalCase + $6.IncludeInstanceMembers = True + $6.IncludeStaticEntities = True + $3.NamingRule = $8 + $8.Name = Attributes + $8.RequiredSuffixes = $9 + $9.String = Attribute + $8.AffectedEntity = CustomAttributes + $8.VisibilityMask = Public + $8.NamingStyle = PascalCase + $8.IncludeInstanceMembers = True + $8.IncludeStaticEntities = True + $3.NamingRule = $10 + $10.Name = Event Arguments + $10.RequiredSuffixes = $11 + $11.String = EventArgs + $10.AffectedEntity = CustomEventArgs + $10.VisibilityMask = Public + $10.NamingStyle = PascalCase + $10.IncludeInstanceMembers = True + $10.IncludeStaticEntities = True + $3.NamingRule = $12 + $12.Name = Exceptions + $12.RequiredSuffixes = $13 + $13.String = Exception + $12.AffectedEntity = CustomExceptions + $12.VisibilityMask = VisibilityMask + $12.NamingStyle = PascalCase + $12.IncludeInstanceMembers = True + $12.IncludeStaticEntities = True + $3.NamingRule = $14 + $14.Name = Methods + $14.AffectedEntity = Methods + $14.VisibilityMask = Protected, Public + $14.NamingStyle = PascalCase + $14.IncludeInstanceMembers = True + $14.IncludeStaticEntities = True + $3.NamingRule = $15 + $15.Name = Static Readonly Fields + $15.AffectedEntity = ReadonlyField + $15.VisibilityMask = Protected, Public + $15.NamingStyle = PascalCase + $15.IncludeInstanceMembers = False + $15.IncludeStaticEntities = True + $3.NamingRule = $16 + $16.Name = Fields + $16.AffectedEntity = Field + $16.VisibilityMask = Protected, Public + $16.NamingStyle = PascalCase + $16.IncludeInstanceMembers = True + $16.IncludeStaticEntities = True + $3.NamingRule = $17 + $17.Name = ReadOnly Fields + $17.AffectedEntity = ReadonlyField + $17.VisibilityMask = Protected, Public + $17.NamingStyle = PascalCase + $17.IncludeInstanceMembers = True + $17.IncludeStaticEntities = False + $3.NamingRule = $18 + $18.Name = Constant Fields + $18.AffectedEntity = ConstantField + $18.VisibilityMask = Protected, Public + $18.NamingStyle = PascalCase + $18.IncludeInstanceMembers = True + $18.IncludeStaticEntities = True + $3.NamingRule = $19 + $19.Name = Properties + $19.AffectedEntity = Property + $19.VisibilityMask = Protected, Public + $19.NamingStyle = PascalCase + $19.IncludeInstanceMembers = True + $19.IncludeStaticEntities = True + $3.NamingRule = $20 + $20.Name = Events + $20.AffectedEntity = Event + $20.VisibilityMask = Protected, Public + $20.NamingStyle = PascalCase + $20.IncludeInstanceMembers = True + $20.IncludeStaticEntities = True + $3.NamingRule = $21 + $21.Name = Enum Members + $21.AffectedEntity = EnumMember + $21.VisibilityMask = VisibilityMask + $21.NamingStyle = PascalCase + $21.IncludeInstanceMembers = True + $21.IncludeStaticEntities = True + $3.NamingRule = $22 + $22.Name = Parameters + $22.AffectedEntity = Parameter + $22.VisibilityMask = VisibilityMask + $22.NamingStyle = CamelCase + $22.IncludeInstanceMembers = True + $22.IncludeStaticEntities = True + $3.NamingRule = $23 + $23.Name = Type Parameters + $23.RequiredPrefixes = $24 + $24.String = T + $23.AffectedEntity = TypeParameter + $23.VisibilityMask = VisibilityMask + $23.NamingStyle = PascalCase + $23.IncludeInstanceMembers = True + $23.IncludeStaticEntities = True + description = @Learning about threads and C#\nSee: http://parallelpatterns.codeplex.com/ + EndGlobalSection +EndGlobal diff --git a/Allgemeines/Threads/Threads/Chapter2.cs b/Allgemeines/Threads/Threads/Chapter2.cs new file mode 100644 index 0000000..fc50172 --- /dev/null +++ b/Allgemeines/Threads/Threads/Chapter2.cs @@ -0,0 +1,319 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Collections.Concurrent; + +namespace Threads +{ + /// + /// + /// Chapter2. + /// Parallel Loops + /// + /// Taken from http://msdn.microsoft.com/en-us/library/ff963552.aspx + /// + /// + public class Chapter2 + { + public static void Test() + { + int n = 10; + + Console.WriteLine("For"); + for (int i = 0; i < n; i++) + { + Console.WriteLine("For {0}", i); + Thread.Sleep(500); + } + + + Console.WriteLine("Parallel.For"); + Parallel.For(0, n, i => + { + Console.WriteLine("Parallel.For {0}", i); + Thread.Sleep(500); + }); + + + Console.WriteLine("Parallel.ForEach"); + List listOfStrings = new List(); + listOfStrings.Add("one"); + listOfStrings.Add("two"); + listOfStrings.Add("three"); + listOfStrings.Add("four"); + Parallel.ForEach(listOfStrings, stringnumber => + { + Console.WriteLine("Parallel.ForEach {0}", stringnumber); + Thread.Sleep(500); + }); + + + Console.WriteLine("LINQ"); + var anonymousFromListOfStrings = from stringnumber in listOfStrings select new { Original = stringnumber }; + // Use ForAll if you need to iterate over a PLINQ result. Don't use ForEach in this case. :( + // I should have used: anonymousFromListOfStrings.ForAll See example at the end of this class :) + Parallel.ForEach(anonymousFromListOfStrings, stringnumber => + { + Console.WriteLine("LINQ {0}", stringnumber); + Thread.Sleep(500); + }); + var stringsFromListOfString = listOfStrings.Select(stringnumber => new String(stringnumber.ToCharArray())); + // Use ForAll if you need to iterate over a PLINQ result. Don't use ForEach in this case. :( + // I should have used: stringsFromListOfString.ForAll See example at the end of this class :) + Parallel.ForEach(stringsFromListOfString, stringnumber => + { + Console.WriteLine("LINQ {0}", stringnumber); + Thread.Sleep(500); + }); + + + Console.WriteLine("PLINQ"); + anonymousFromListOfStrings = from stringnumber in listOfStrings.AsParallel() select new { Original = stringnumber }; + Parallel.ForEach(anonymousFromListOfStrings, stringnumber => + { + Console.WriteLine("PLINQ {0}", stringnumber); + Thread.Sleep(500); + }); + stringsFromListOfString = listOfStrings.AsParallel().Select(stringnumber => new String(stringnumber.ToCharArray())); + Parallel.ForEach(stringsFromListOfString, stringnumber => + { + Console.WriteLine("PLINQ {0}", stringnumber); + Thread.Sleep(500); + }); + + + Console.WriteLine("PLINQ, ForAll"); + listOfStrings.AsParallel().ForAll(stringnumber => + { + Console.WriteLine("PLINQ, ForAll {0}", stringnumber); + Thread.Sleep(500); + }); + + + Console.WriteLine("Parallel Break"); + // During the processing of a call to the Break method, iterations with an index + // value less than the current index will be allowed to start (if they have not already started), + // but iterations with an index value greater than the current index will not be started. + // This ensures that all iterations below the break point will complete. + n = 20; + var loopResult = Parallel.For(0, n, (i, loopState) => + { + // What should I use? LowestBreakIteration or ShouldExitCurrentIteration? :( + if (loopState.LowestBreakIteration.HasValue || loopState.ShouldExitCurrentIteration) + { + Console.WriteLine("Parallel Break Check State {0}", i); + loopState.Break(); + return; + } + + Console.WriteLine("Parallel Break {0}", i); + Thread.Sleep(500); + + if (i == 10) + { + loopState.Break(); + return; + } + }); + if (!loopResult.IsCompleted && loopResult.LowestBreakIteration.HasValue) + { + // You will never see this code becasue the loopResult IS ALWAYS COMPLETED :/ + Console.WriteLine("Parallel Break (don't see me), loop encountered a break at {0}", loopResult.LowestBreakIteration.Value); + } + // loopResult IS ALWAYS COMPLETED!!! Break must be like a shutdown in Java Executor? + // The Tasks in the Queue will be executed but the Queue does not accept more tasks after shutdown. + // In this case are the Tasks the lambda expression? + // Depending on where you check the loopState in the lambda expression this could be a problem, + // you end up thinking the parallel for is completed when it is not. :/ + Console.WriteLine("Parallel Break IsCompleted {0}", loopResult.IsCompleted); + Console.WriteLine("Parallel Break LowestBreakIteration {0}", loopResult.LowestBreakIteration.HasValue); + if (loopResult.LowestBreakIteration.HasValue) + { + Console.WriteLine("Parallel Break, loop encountered a break at {0}", loopResult.LowestBreakIteration.Value); + } + + + Console.WriteLine("Parallel Stop"); + loopResult = Parallel.For(0, n, (i, loopState) => + { + // What should I use? LowestBreakIteration, ShouldExitCurrentIteration or IsStopped? :( + if (loopState.LowestBreakIteration.HasValue || loopState.ShouldExitCurrentIteration || loopState.IsStopped) + { + Console.WriteLine("Parallel Stop Check State {0}", i); + loopState.Stop(); + return; + } + + Console.WriteLine("Parallel Stop {0}", i); + Thread.Sleep(500); + + if (i == 10) + { + loopState.Stop(); + return; + } + }); + // loopResult IS ALWAYS NOT COMPLETED!!! Stop must be like a shutdownNow in Java Executor? + // The Tasks in the Queue will NOT be executed and the Queue does not accept more tasks after shutdownNow. + // In this case are the Tasks the lambda expression? + if (!loopResult.IsCompleted && !loopResult.LowestBreakIteration.HasValue) + { + // When the Stop method is called, the index value of the iteration that caused the stop isn't available. :( + Console.WriteLine("Parallel Stop, loop was stopped"); + } + Console.WriteLine("Parallel Stop IsCompleted {0}", loopResult.IsCompleted); + Console.WriteLine("Parallel Stop LowestBreakIteration {0}", loopResult.LowestBreakIteration.HasValue); + + + Console.WriteLine("CancellationToken"); + var cts = new CancellationTokenSource(); + cts.CancelAfter(600); + CancellationToken token = cts.Token; + var options = new ParallelOptions { CancellationToken = token }; + try + { + Parallel.For(0, n, options, (i) => + { + // optionally check to see if cancellation happened + if (token.IsCancellationRequested) + { + Console.WriteLine("CancellationToken Check State {0}", i); + // optionally exit this iteration early + return; + } + + Console.WriteLine("CancellationToken {0}", i); + Thread.Sleep(500); + }); + } + catch (OperationCanceledException ex) + { + // I never see this message. Where is my exception? :( + Console.WriteLine("CancellationToken Exception {0}", ex.StackTrace); + } + // If external cancellation has been signaled and your loop has called either the + // Break or the Stop method of the ParallelLoopState object, a race occurs to see which + // will be recognized first. The parallel loop will either throw an OperationCanceledException + // or it will terminate using the mechanism for Break and Stop that is described in the section, + // "Breaking Out of Loops Early," earlier in this chapter. + + + // If the body of a parallel loop throws an unhandled exception, the parallel loop no longer begins + // any new steps. By default, iterations that are executing at the time of the exception, + // other than the iteration that threw the exception, will complete. After they finish, the parallel + // loop will throw an exception in the context of the thread that invoked it. Long-running iterations + // may want to test to see whether an exception is pending in another iteration. They can do this + // with the ParallelLoopState class's IsExceptional property. This property returns true if an exception is pending. + + // Because more than one exception may occur during parallel execution, exceptions are grouped using + // an exception type known as an aggregate exception. The AggregateException class has an + // InnerExceptions property that contains a collection of all the exceptions that occurred during + // the execution of the parallel loop. Because the loop runs in parallel, there may be more than one exception. + + // Exceptions take priority over external cancellations and terminations of a loop initiated by calling the + // Break or Stop methods of the ParallelLoopState object. + + + Console.WriteLine("Partitioner Default"); + Parallel.ForEach(Partitioner.Create(0, n), range => + { + Console.WriteLine("Partitioner Default range {0} {1}", range.Item1, range.Item2); + for (int i = range.Item1; i < range.Item2; i++) + { + // very small, equally sized blocks of work + Console.WriteLine("Partitioner Default {0}", i); + } + }); + // The number of ranges that will be created by a Partitioner object depends on the number of cores in your computer. + // The default number of ranges is approximately three times the number of those cores. + + + // If you know how big you want your ranges to be, you can use an overloaded version of the Partitioner.Create method + // that allows you to specify the size of each range. Here's an example. + Console.WriteLine("Partitioner Custom"); + Parallel.ForEach(Partitioner.Create(0, n, n/2), range => + { + Console.WriteLine("Partitioner Custom range {0} {1}", range.Item1, range.Item2); + for (int i = range.Item1; i < range.Item2; i++) + { + // small, equally sized blocks of work + Console.WriteLine("Partitioner Custom {0}", i); + } + }); + + + // You can limit the maximum number of tasks used concurrently by specifying the MaxDegreeOfParallelism property of + // a ParallelOptions object. Here is an example + Console.WriteLine("Parallel.For MaxDegreeOfParallelism 2"); + options = new ParallelOptions() { MaxDegreeOfParallelism = 2 }; + Parallel.For(0, n, options, i => + { + Console.WriteLine("Parallel.For MaxDegreeOfParallelism {0}", i); + Thread.Sleep(500); + }); + + + Console.WriteLine("PLINQ, ForAll WithDegreeOfParallelism 2"); + listOfStrings.AsParallel().WithDegreeOfParallelism(2).ForAll(stringnumber => + { + Console.WriteLine("PLINQ, ForAll WithDegreeOfParallelism {0}", stringnumber); + Thread.Sleep(500); + }); + + + // Here's an example that uses one of the overloads of the Parallel.ForEach method. The example uses a Partitioner + // object to decompose the work into relatively large pieces, because the amount of work performed by each step is small, + // and there are a large number of steps. + Console.WriteLine("Task-Local State in a Loop Body"); + int numberOfSteps = 10000000; + double[] results = new double[numberOfSteps]; + Parallel.ForEach(Partitioner.Create(0, numberOfSteps/2), + new ParallelOptions(), + () => { return new Random(); }, + (range, loopState, random) => + { + for (int i = range.Item1; i < range.Item2; i++) + results[i] = random.NextDouble(); + return random; + }, + _ => {} + ); + + + // Console.WriteLine("Custom Task Scheduler for a Parallel Loop"); + // TaskScheduler myScheduler = How???? :( + // options = new ParallelOptions() { TaskScheduler = myScheduler }; + // Parallel.For(0, n, options, i => + // { + // Console.WriteLine("Custom Task Scheduler for a Parallel Loop {0}", i); + // Thread.Sleep(500); + // }); + + + // Use ForAll if you need to iterate over a PLINQ result. Don't use ForEach in this case. + Console.WriteLine("PLINQ's ForAll the same as Parallel.ForEach"); + var plinqforall = from stringnumber in listOfStrings.AsParallel() select new String(stringnumber.ToCharArray()); + // This is what I should have used instead :) + plinqforall.ForAll(stringnumber => + { + Console.WriteLine("PLINQ's ForAll the same as Parallel.ForEach {0}", stringnumber); + Thread.Sleep(500); + }); + + + + // Be careful if you use parallel loops with individual steps that take several seconds or more. + // This can occur with I/O-bound workloads as well as lengthy calculations. If the loops take a long + // time, you may experience an unbounded growth of worker threads due to a heuristic for preventing thread starvation + // that's used by the .NET ThreadPool class's thread injection logic. This heuristic steadily increases the number + // of worker threads when work items of the current pool run for long periods of time. The motivation is to add + // more threads in cases where everything in the thread pool is blocked. Unfortunately, if work is actually proceeding, + // more threads may not necessarily be what you want. The .NET Framework can't distinguish between these two situations. + + //If the individual steps of your loop take a long time, you may see more worker threads than you intend. + } + } +} + diff --git a/Allgemeines/Threads/Threads/Program.cs b/Allgemeines/Threads/Threads/Program.cs new file mode 100644 index 0000000..641fd36 --- /dev/null +++ b/Allgemeines/Threads/Threads/Program.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; +using System.Threading; +using System.Collections.Generic; +using System.Linq; +using System.Collections.Concurrent; + +namespace Threads +{ + class MainClass + { + public static void Main(string[] args) + { + Chapter2.Test(); + } + } +} diff --git a/Allgemeines/Threads/Threads/Properties/AssemblyInfo.cs b/Allgemeines/Threads/Threads/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bd10a4c --- /dev/null +++ b/Allgemeines/Threads/Threads/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("Threads")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("gumartinm.name")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("gumartinm.name")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/Allgemeines/Threads/Threads/Threads.csproj b/Allgemeines/Threads/Threads/Threads.csproj new file mode 100644 index 0000000..4a0a00c --- /dev/null +++ b/Allgemeines/Threads/Threads/Threads.csproj @@ -0,0 +1,45 @@ + + + + Debug + x86 + 12.0.0 + 2.0 + {CED02136-FA8A-4E92-9BC6-3340DA89C3A4} + Exe + Threads + Threads + v4.5 + Learning about threads and C# +See: http://parallelpatterns.codeplex.com/ + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + \ No newline at end of file -- 2.1.4