├── .gitattributes
├── .gitignore
├── ParallelDemo.sln
├── ParallelDemo
├── App.config
├── App.xaml
├── App.xaml.cs
├── AsyncDemo
│ └── AsyncDemo.cs
├── BlockingLazy
│ └── SimpleBlockingLazy.cs
├── BlockingQueue
│ └── SimpleBlockingQueue.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── ParallelDemo.csproj
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── ReadWriteLock
│ ├── ReadWriteLockSlimEx.cs
│ ├── SimpleReadWriteLock.cs
│ └── TestRWLock.cs
└── packages.config
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | bld/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # Roslyn cache directories
20 | *.ide/
21 |
22 | # MSTest test Results
23 | [Tt]est[Rr]esult*/
24 | [Bb]uild[Ll]og.*
25 |
26 | #NUNIT
27 | *.VisualState.xml
28 | TestResult.xml
29 |
30 | # Build Results of an ATL Project
31 | [Dd]ebugPS/
32 | [Rr]eleasePS/
33 | dlldata.c
34 |
35 | *_i.c
36 | *_p.c
37 | *_i.h
38 | *.ilk
39 | *.meta
40 | *.obj
41 | *.pch
42 | *.pdb
43 | *.pgc
44 | *.pgd
45 | *.rsp
46 | *.sbr
47 | *.tlb
48 | *.tli
49 | *.tlh
50 | *.tmp
51 | *.tmp_proj
52 | *.log
53 | *.vspscc
54 | *.vssscc
55 | .builds
56 | *.pidb
57 | *.svclog
58 | *.scc
59 |
60 | # Chutzpah Test files
61 | _Chutzpah*
62 |
63 | # Visual C++ cache files
64 | ipch/
65 | *.aps
66 | *.ncb
67 | *.opensdf
68 | *.sdf
69 | *.cachefile
70 |
71 | # Visual Studio profiler
72 | *.psess
73 | *.vsp
74 | *.vspx
75 |
76 | # TFS 2012 Local Workspace
77 | $tf/
78 |
79 | # Guidance Automation Toolkit
80 | *.gpState
81 |
82 | # ReSharper is a .NET coding add-in
83 | _ReSharper*/
84 | *.[Rr]e[Ss]harper
85 | *.DotSettings.user
86 |
87 | # JustCode is a .NET coding addin-in
88 | .JustCode
89 |
90 | # TeamCity is a build add-in
91 | _TeamCity*
92 |
93 | # DotCover is a Code Coverage Tool
94 | *.dotCover
95 |
96 | # NCrunch
97 | _NCrunch_*
98 | .*crunch*.local.xml
99 |
100 | # MightyMoose
101 | *.mm.*
102 | AutoTest.Net/
103 |
104 | # Web workbench (sass)
105 | .sass-cache/
106 |
107 | # Installshield output folder
108 | [Ee]xpress/
109 |
110 | # DocProject is a documentation generator add-in
111 | DocProject/buildhelp/
112 | DocProject/Help/*.HxT
113 | DocProject/Help/*.HxC
114 | DocProject/Help/*.hhc
115 | DocProject/Help/*.hhk
116 | DocProject/Help/*.hhp
117 | DocProject/Help/Html2
118 | DocProject/Help/html
119 |
120 | # Click-Once directory
121 | publish/
122 |
123 | # Publish Web Output
124 | *.[Pp]ublish.xml
125 | *.azurePubxml
126 | ## TODO: Comment the next line if you want to checkin your
127 | ## web deploy settings but do note that will include unencrypted
128 | ## passwords
129 | #*.pubxml
130 |
131 | # NuGet Packages Directory
132 | packages/*
133 | ## TODO: If the tool you use requires repositories.config
134 | ## uncomment the next line
135 | #!packages/repositories.config
136 |
137 | # Enable "build/" folder in the NuGet Packages folder since
138 | # NuGet packages use it for MSBuild targets.
139 | # This line needs to be after the ignore of the build folder
140 | # (and the packages folder if the line above has been uncommented)
141 | !packages/build/
142 |
143 | # Windows Azure Build Output
144 | csx/
145 | *.build.csdef
146 |
147 | # Windows Store app package directory
148 | AppPackages/
149 |
150 | # Others
151 | sql/
152 | *.Cache
153 | ClientBin/
154 | [Ss]tyle[Cc]op.*
155 | ~$*
156 | *~
157 | *.dbmdl
158 | *.dbproj.schemaview
159 | *.pfx
160 | *.publishsettings
161 | node_modules/
162 |
163 | # RIA/Silverlight projects
164 | Generated_Code/
165 |
166 | # Backup & report files from converting an old project file
167 | # to a newer Visual Studio version. Backup files are not needed,
168 | # because we have git ;-)
169 | _UpgradeReport_Files/
170 | Backup*/
171 | UpgradeLog*.XML
172 | UpgradeLog*.htm
173 |
174 | # SQL Server files
175 | *.mdf
176 | *.ldf
177 |
178 | # Business Intelligence projects
179 | *.rdl.data
180 | *.bim.layout
181 | *.bim_*.settings
182 |
183 | # Microsoft Fakes
184 | FakesAssemblies/
185 |
186 | # LightSwitch generated files
187 | GeneratedArtifacts/
188 | _Pvt_Extensions/
189 | ModelManifest.xml
--------------------------------------------------------------------------------
/ParallelDemo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParallelDemo", "ParallelDemo\ParallelDemo.csproj", "{5BC69670-B51C-4E2B-A167-E8C2D6C07EB0}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {5BC69670-B51C-4E2B-A167-E8C2D6C07EB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {5BC69670-B51C-4E2B-A167-E8C2D6C07EB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {5BC69670-B51C-4E2B-A167-E8C2D6C07EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {5BC69670-B51C-4E2B-A167-E8C2D6C07EB0}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/ParallelDemo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ParallelDemo/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ParallelDemo/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace ParallelDemo
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ParallelDemo/AsyncDemo/AsyncDemo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using NUnit.Framework;
5 | // ReSharper disable LocalizableElement
6 |
7 | namespace ParallelDemo.AsyncDemo
8 | {
9 | public class AsyncDemo
10 | {
11 | int A()
12 | {
13 | Console.WriteLine("A: {0}" , Thread.CurrentThread.ManagedThreadId);
14 | return 1;
15 | }
16 |
17 | Task B(int p)
18 | {
19 | return Task.Run(() =>
20 | {
21 | Console.WriteLine("B: {0}", Thread.CurrentThread.ManagedThreadId);
22 | return p + 2;
23 | });
24 | }
25 |
26 | // async Task B(int p)
27 | // {
28 | // return p + 2;
29 | // }
30 |
31 |
32 | int C(int p)
33 | {
34 | Console.WriteLine("C: {0}", Thread.CurrentThread.ManagedThreadId);
35 | return p + 3;
36 | }
37 |
38 |
39 | async Task AsyncMethod()
40 | {
41 | int a = A();
42 | int b = await B(a).ConfigureAwait(false);
43 | return C(b);
44 | }
45 |
46 |
47 |
48 |
49 | [Test]
50 | public void TestAsync()
51 | {
52 | Console.WriteLine("Started: {0}", Thread.CurrentThread.ManagedThreadId);
53 | var task = EmulatedAsyncMethod();
54 | Assert.AreEqual(6, task.Result);
55 | }
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Task EmulatedAsyncMethod()
66 | {
67 | int a = A();
68 | return Task.Factory.StartNew(() =>
69 | {
70 | return B(a).ContinueWith(_ => C(_.Result), TaskContinuationOptions.AttachedToParent);
71 | }).ContinueWith(task =>
72 | {
73 | Assert.True(task.IsCompleted);
74 | Assert.True(task.Result.IsCompleted);
75 | return task.Result.Result;
76 | });
77 | }
78 |
79 | }
80 | }
--------------------------------------------------------------------------------
/ParallelDemo/BlockingLazy/SimpleBlockingLazy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 |
5 | namespace ParallelDemo.BlockingLazy
6 | {
7 | public class SimpleBlockingLazy where T:class
8 | {
9 | private readonly object myLock = new object();
10 | private readonly Func myProducer;
11 |
12 | private T myValue;
13 |
14 | public SimpleBlockingLazy(Func producer)
15 | {
16 | myProducer = producer;
17 | }
18 |
19 | public T Value
20 | {
21 | get
22 | {
23 | var res = myValue;
24 | if (res != null) return res;
25 |
26 | lock (myLock)
27 | {
28 | if (myValue != null) return myValue;
29 |
30 | res = myProducer();
31 | Thread.MemoryBarrier();
32 | myValue = res;
33 |
34 | Debug.Assert(myValue != null);
35 | return myValue;
36 | }
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/ParallelDemo/BlockingQueue/SimpleBlockingQueue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Concurrent;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Threading;
7 |
8 | namespace ParallelDemo.BlockingQueue
9 | {
10 | ///
11 | /// Concurrent blocking queue with predefined size. All operations are thread safe. Operations like ToArray
or GetEnumerator
12 | /// takes queue snapshot at some moment.
13 | ///
14 | ///
15 |
16 | public class SimpleBlockingQueue : IProducerConsumerCollection where T : class
17 | {
18 | private const long BlockingOperationInProgressMarker = -1;
19 |
20 | private readonly T[] myQueue;
21 | private readonly long myMask;
22 | private readonly int mySize;
23 |
24 | private long myHead;
25 | private long myTail;
26 |
27 | ///
28 | /// Constructs queue with predefined size. Queue size is defined logariphmically. Queue is not growing, nor shrinking.
29 | ///
30 | /// From 1 to 20. Real queue size will be 1<<.
31 | public SimpleBlockingQueue(int logMaxSize)
32 | {
33 | if (logMaxSize <= 1 || logMaxSize > 20) throw new ArgumentException("Bad logsize:" + logMaxSize);
34 |
35 | mySize = 1 << logMaxSize;
36 | myQueue = new T[mySize];
37 | myMask = mySize - 1;
38 | }
39 |
40 |
41 | #region Nonblocking API
42 |
43 | ///
44 | /// Enqueues to the tail of the queue. Increases tail.
45 | ///
46 | ///
47 | ///
48 | public bool TryAdd(T t)
49 | {
50 | if (t == null) throw new ArgumentNullException("t");
51 |
52 | while (true)
53 | {
54 | //prologue
55 | var head = Interlocked.Read(ref myHead);
56 | var tail = Interlocked.Read(ref myTail);
57 | // if (head == BlockingOperationInProgressMarker || tail == BlockingOperationInProgressMarker)
58 | // {
59 | // Thread.Yield();
60 | // continue;
61 | // }
62 |
63 | //check
64 | if (tail - head >= mySize) return false;
65 |
66 | //set
67 | if (tail == Interlocked.CompareExchange(ref myTail, tail + 1, tail))
68 | {
69 | var idx = tail & myMask;
70 | myQueue[idx] = t;
71 | return true;
72 | }
73 |
74 | Thread.Yield();
75 | }
76 | }
77 |
78 | ///
79 | /// Dequeues from the head of the queue. Moves head.
80 | ///
81 | ///
82 | ///
83 | public bool TryDequeue(out T res)
84 | {
85 | while (true)
86 | {
87 | //prologue
88 | var head = Interlocked.Read(ref myHead);
89 | var tail = Interlocked.Read(ref myTail);
90 | if (head == BlockingOperationInProgressMarker || tail == BlockingOperationInProgressMarker)
91 | {
92 | Thread.Yield();
93 | continue;
94 | }
95 |
96 | //check
97 | if (head >= tail)
98 | {
99 | res = null;
100 | return false;
101 | }
102 |
103 | //set
104 | var queue = myQueue;
105 | var idx = head & myMask;
106 | res = queue[idx];
107 | if (res != null && head == Interlocked.CompareExchange(ref myHead, head + 1, head))
108 | {
109 | Interlocked.CompareExchange(ref queue[idx], null, res); //if some producer already set this index with new value, don't clear;
110 | return true;
111 | }
112 |
113 | Thread.Yield();
114 | }
115 | }
116 |
117 | ///
118 | /// Peeks from the head of the queue. Leaves head untouched. Sequential peeks will return same result if no other thread dequeues or clears simultaneously.
119 | ///
120 | ///
121 | ///
122 | public bool TryPeek(out T res)
123 | {
124 | while (true)
125 | {
126 | //prologue
127 | var head = Interlocked.Read(ref myHead);
128 | var tail = Interlocked.Read(ref myTail);
129 | if (head == BlockingOperationInProgressMarker || tail == BlockingOperationInProgressMarker)
130 | {
131 | Thread.Yield();
132 | continue;
133 | }
134 |
135 | //check
136 | if (head >= tail)
137 | {
138 | res = null;
139 | return false;
140 | }
141 |
142 | //op
143 | var queue = myQueue;
144 | var idx = head & myMask;
145 | if ((res = queue[idx]) != null) return true;
146 |
147 | Thread.Yield();
148 | }
149 | }
150 |
151 |
152 | ///
153 | /// Number of elements in queue snapshot.
154 | ///
155 | public int Count
156 | {
157 | get
158 | {
159 | while (true)
160 | {
161 | //prologue
162 | var head = Interlocked.Read(ref myHead);
163 | var tail = Interlocked.Read(ref myTail);
164 | if (head != BlockingOperationInProgressMarker && tail != BlockingOperationInProgressMarker)
165 | {
166 | return (int)(tail - head);
167 | }
168 | Thread.Yield();
169 | }
170 | }
171 | }
172 |
173 | #endregion
174 |
175 |
176 |
177 | #region Blocking API
178 |
179 | public void CopyTo(T[] array, int index)
180 | {
181 | DoImmutableBlockingOp((head, tail) =>
182 | {
183 | while (head < tail) array[index++] = myQueue[head++ & myMask];
184 | });
185 | }
186 |
187 |
188 | public T[] ToArray()
189 | {
190 | return DoImmutableBlockingOp((head, tail) =>
191 | {
192 | var index = 0;
193 | var array = new T[tail - head];
194 | while (head < tail) array[index++] = myQueue[head++ & myMask];
195 | return array;
196 | });
197 | }
198 |
199 |
200 | public void CopyTo(Array array, int index)
201 | {
202 | DoImmutableBlockingOp((head, tail) =>
203 | {
204 | while (head < tail) array.SetValue(myQueue[head++ & myMask], index++);
205 | });
206 | }
207 |
208 | public void Clear()
209 | {
210 | {
211 | while (true)
212 | {
213 | //prologue
214 | var head = Interlocked.Read(ref myHead);
215 | var tail = Interlocked.Read(ref myTail);
216 | if (head == BlockingOperationInProgressMarker || tail == BlockingOperationInProgressMarker)
217 | {
218 | Thread.Yield();
219 | continue;
220 | }
221 |
222 | //set
223 | if (head == Interlocked.CompareExchange(ref myHead, BlockingOperationInProgressMarker, head))
224 | {
225 | if (tail == Interlocked.CompareExchange(ref myTail, BlockingOperationInProgressMarker, tail))
226 | {
227 | try
228 | {
229 | Array.Clear(myQueue, 0, mySize);
230 | }
231 | finally
232 | {
233 | var prevtail = Interlocked.Exchange(ref myTail, 0);
234 | var prevhead = Interlocked.Exchange(ref myHead, 0);
235 | Debug.Assert(prevtail == BlockingOperationInProgressMarker, "Invalid state: tail");
236 | Debug.Assert(prevhead == BlockingOperationInProgressMarker, "Invalid state: head");
237 | }
238 | return;
239 | }
240 | else
241 | {
242 | var prevhead = Interlocked.Exchange(ref myHead, head);
243 | Debug.Assert(prevhead == BlockingOperationInProgressMarker, "Invalid state: head");
244 | }
245 | }
246 |
247 |
248 | //continue
249 | Thread.Yield();
250 | }
251 | }
252 | }
253 |
254 |
255 | private void DoImmutableBlockingOp(Action op)
256 | {
257 | DoImmutableBlockingOp