├── DataStructures
├── DataStructures.csproj
├── IMetricsCounter.cs
├── ConcurrentDictionaryOnlyMetricsCounter.cs
├── ConcurrentDictionaryWithCounterMetricsCounter.cs
└── LockingMetricsCounter.cs
├── DataStructures.Tests
├── DataStructures.Tests.csproj
└── MetricsCounterTest.cs
├── Exercises.sln
├── .gitattributes
└── .gitignore
/DataStructures/DataStructures.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DataStructures/IMetricsCounter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace DataStructures
4 | {
5 | public interface IMetricsCounter : IEnumerable>
6 | {
7 | ///
8 | /// Increments the counter under the specific . If the key does not exist, it should be initialized with 1.
9 | ///
10 | /// The key
11 | void Increment(string key);
12 | }
13 | }
--------------------------------------------------------------------------------
/DataStructures.Tests/DataStructures.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/DataStructures/ConcurrentDictionaryOnlyMetricsCounter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 |
4 | namespace DataStructures
5 | {
6 | public class ConcurrentDictionaryOnlyMetricsCounter : IMetricsCounter
7 | {
8 | // Implement this class using only ConcurrentDictionary.
9 | // Use methods that change the state atomically to ensure that everything is counted properly.
10 | // This task does not require using any Interlocked, or Volatile methods. The only required API is provided by the ConcurrentDictionary
11 |
12 | public IEnumerator> GetEnumerator()
13 | {
14 | throw new System.NotImplementedException();
15 | }
16 |
17 | public void Increment(string key)
18 | {
19 | throw new System.NotImplementedException();
20 | }
21 |
22 | IEnumerator IEnumerable.GetEnumerator()
23 | {
24 | return GetEnumerator();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/DataStructures/ConcurrentDictionaryWithCounterMetricsCounter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 |
5 | namespace DataStructures
6 | {
7 | public class ConcurrentDictionaryWithCounterMetricsCounter : IMetricsCounter
8 | {
9 | // Implement this class using ConcurrentDictionary and the provided AtomicCounter class.
10 | // AtomicCounter should be created only once per key, then its Increment method should be used.
11 |
12 | public IEnumerator> GetEnumerator()
13 | {
14 | throw new System.NotImplementedException();
15 | }
16 |
17 | public void Increment(string key)
18 | {
19 | throw new System.NotImplementedException();
20 | }
21 |
22 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
23 |
24 | public class AtomicCounter
25 | {
26 | int value;
27 |
28 | public void Increment() => Interlocked.Increment(ref value);
29 |
30 | public int Count => Volatile.Read(ref value);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/DataStructures/LockingMetricsCounter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace DataStructures
6 | {
7 | public class LockingMetricsCounter : IMetricsCounter
8 | {
9 | readonly Dictionary counters = new Dictionary();
10 |
11 | ///
12 | /// Increments the counter under the specific . If the key does not exist, it should be initialized with 1.
13 | ///
14 | /// The key
15 | public void Increment(string key)
16 | {
17 | lock (counters)
18 | {
19 | if (counters.TryGetValue(key, out var counter))
20 | {
21 | counters[key] = counter + 1;
22 | }
23 | else
24 | {
25 | counters[key] = 1;
26 | }
27 | }
28 | }
29 |
30 | public IEnumerator> GetEnumerator()
31 | {
32 | lock (counters)
33 | {
34 | return counters.ToList().GetEnumerator();
35 | }
36 | }
37 |
38 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Exercises.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30011.22
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStructures", "DataStructures\DataStructures.csproj", "{DC260D9F-BC09-49F4-8E63-B6B4BDC24E10}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStructures.Tests", "DataStructures.Tests\DataStructures.Tests.csproj", "{5B365E69-5F7E-493B-8A8B-E32561AE710F}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {DC260D9F-BC09-49F4-8E63-B6B4BDC24E10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {DC260D9F-BC09-49F4-8E63-B6B4BDC24E10}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {DC260D9F-BC09-49F4-8E63-B6B4BDC24E10}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {DC260D9F-BC09-49F4-8E63-B6B4BDC24E10}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {5B365E69-5F7E-493B-8A8B-E32561AE710F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {5B365E69-5F7E-493B-8A8B-E32561AE710F}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {5B365E69-5F7E-493B-8A8B-E32561AE710F}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {5B365E69-5F7E-493B-8A8B-E32561AE710F}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {32BDB419-2C6C-41C1-9988-5EA1EC2A2145}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/DataStructures.Tests/MetricsCounterTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using NUnit.Framework;
5 |
6 | namespace DataStructures.Tests
7 | {
8 | [TestFixture(typeof(LockingMetricsCounter))]
9 | [TestFixture(typeof(ConcurrentDictionaryWithCounterMetricsCounter))]
10 | [TestFixture(typeof(ConcurrentDictionaryOnlyMetricsCounter))]
11 | public class MetricsCounterTest
12 | where TMetricCounter : IMetricsCounter, new()
13 | {
14 | const int KeyCount = 16;
15 | const int ValueCount = 100000;
16 | const int ConcurrentWriters = 2;
17 |
18 | [Test]
19 | public async Task CountingTest()
20 | {
21 | var originalKeys = Enumerable.Range(0, KeyCount).Select(i => i.ToString()).ToArray();
22 | var keys = Enumerable.Repeat(originalKeys, ConcurrentWriters).SelectMany(m => m).ToArray();
23 |
24 | var starter = new TaskCompletionSource