diff --git a/src/ConcurrentCollections.sln b/src/ConcurrentCollections.sln index 295c310..3f17b1d 100644 --- a/src/ConcurrentCollections.sln +++ b/src/ConcurrentCollections.sln @@ -1,9 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.9 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConcurrentHashSet", "ConcurrentHashSet\ConcurrentHashSet.csproj", "{58063048-C7B0-4F4D-B27E-1ED5F9789AF7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConcurrentHashSet", "ConcurrentHashSet\ConcurrentHashSet.csproj", "{58063048-C7B0-4F4D-B27E-1ED5F9789AF7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConcurrentHashSet.Test", "ConcurrentHashSet.Test\ConcurrentHashSet.Test.csproj", "{A559187C-0971-42CD-AE8C-2BC1132965FE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,8 +17,15 @@ Global {58063048-C7B0-4F4D-B27E-1ED5F9789AF7}.Debug|Any CPU.Build.0 = Debug|Any CPU {58063048-C7B0-4F4D-B27E-1ED5F9789AF7}.Release|Any CPU.ActiveCfg = Release|Any CPU {58063048-C7B0-4F4D-B27E-1ED5F9789AF7}.Release|Any CPU.Build.0 = Release|Any CPU + {A559187C-0971-42CD-AE8C-2BC1132965FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A559187C-0971-42CD-AE8C-2BC1132965FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A559187C-0971-42CD-AE8C-2BC1132965FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A559187C-0971-42CD-AE8C-2BC1132965FE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F8839D42-C1EB-4D5A-8C15-D7A32C0EA9E2} + EndGlobalSection EndGlobal diff --git a/src/ConcurrentHashSet.Test/ConcurrentHashSet.Test.csproj b/src/ConcurrentHashSet.Test/ConcurrentHashSet.Test.csproj new file mode 100644 index 0000000..eec684e --- /dev/null +++ b/src/ConcurrentHashSet.Test/ConcurrentHashSet.Test.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + Always + + + + diff --git a/src/ConcurrentHashSet.Test/Program.cs b/src/ConcurrentHashSet.Test/Program.cs new file mode 100644 index 0000000..65d5f02 --- /dev/null +++ b/src/ConcurrentHashSet.Test/Program.cs @@ -0,0 +1,68 @@ +// See https://aka.ms/new-console-template for more information +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Text; + +Console.WriteLine("ConcurrentHashSet test now!"); + +try +{ + var watch1 = new Stopwatch(); + watch1.Start(); + + int total1 = 0; + ConcurrentHashSet hashSet1 = new ConcurrentHashSet(); + using (var sr = new StreamReader("stopwords_en_nltk.txt", Encoding.UTF8)) + { + string? line = null; + while ((line = sr.ReadLine()) != null) + { + if(!hashSet1.Contains(line)) + hashSet1.Add(line); + System.Threading.Interlocked.Add(ref total1, 1); + } + } + watch1.Stop(); + Console.WriteLine($"ConcurrentHashSet: stopwords_en_nltk.txt load finished, Count:{hashSet1.Count}, total:{total1}. time elapsed {watch1.ElapsedMilliseconds} ms"); + + var watch2 = new Stopwatch(); + watch2.Start(); + int total2 = 0; + ConcurrentHashSet hashSet2 = new ConcurrentHashSet(); + System.Threading.Tasks.Parallel.ForEach(File.ReadLines("stopwords_en_nltk.txt", Encoding.UTF8), (line, _, lineNumber) => + { + if (!hashSet2.Contains(line)) + hashSet2.Add(line); + System.Threading.Interlocked.Add(ref total2, 1); + }); + + watch2.Stop(); + Console.WriteLine($"ConcurrentHashSet: stopwords_en_nltk.txt load as parallel finished, Count:{hashSet2.Count}, total:{total2}. time elapsed {watch2.ElapsedMilliseconds} ms"); + + var watch3 = new Stopwatch(); + watch3.Start(); + int total3 = 0; + ConcurrentBag bag = new ConcurrentBag(); + System.Threading.Tasks.Parallel.ForEach(File.ReadLines("stopwords_en_nltk.txt", Encoding.UTF8), (line, _, lineNumber) => + { + if (!bag.Contains(line)) + bag.Add(line); + System.Threading.Interlocked.Add(ref total3, 1); + }); + + watch3.Stop(); + + Console.WriteLine($"ConcurrentBag: stopwords_en_nltk.txt load as parallel finished, Count:{bag.Count}, total:{total3}. time elapsed {watch3.ElapsedMilliseconds} ms"); + + Console.WriteLine("Press any key to exit."); +} +catch (IOException e) +{ + Console.WriteLine(string.Format("{0} load failure, reason: {1}", "stopwords_en_nltk.txt", e.Message)); +} +catch (Exception e) +{ + Console.WriteLine(e.Message); +} + +Console.ReadKey(); diff --git a/src/ConcurrentHashSet.Test/stopwords_en_nltk.txt b/src/ConcurrentHashSet.Test/stopwords_en_nltk.txt new file mode 100644 index 0000000..49dd96e --- /dev/null +++ b/src/ConcurrentHashSet.Test/stopwords_en_nltk.txt @@ -0,0 +1,127 @@ +i +me +my +myself +we +our +ours +ourselves +you +your +yours +yourself +yourselves +he +him +his +himself +she +her +hers +herself +it +its +itself +they +them +their +theirs +themselves +what +which +who +whom +this +that +these +those +am +is +are +was +were +be +been +being +have +has +had +having +do +does +did +doing +a +an +the +and +but +if +or +because +as +until +while +of +at +by +for +with +about +against +between +into +through +during +before +after +above +below +to +from +up +down +in +out +on +off +over +under +again +further +then +once +here +there +when +where +why +how +all +any +both +each +few +more +most +other +some +such +no +nor +not +only +own +same +so +than +too +very +s +t +can +will +just +don +should +now \ No newline at end of file diff --git a/src/ConcurrentHashSet/ConcurrentHashSet.cs b/src/ConcurrentHashSet/ConcurrentHashSet.cs index 8876f6d..e74ce61 100644 --- a/src/ConcurrentHashSet/ConcurrentHashSet.cs +++ b/src/ConcurrentHashSet/ConcurrentHashSet.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Threading; -namespace ConcurrentCollections +namespace System.Collections.Concurrent { /// /// Represents a thread-safe hash-based unique collection. @@ -274,7 +271,7 @@ private ConcurrentHashSet(int concurrencyLevel, int capacity, bool growLockArray /// The /// contains too many items. public bool Add(T item) => - AddInternal(item, _comparer.GetHashCode(item), true); + AddInternal(item, true); // , _comparer.GetHashCode(item) /// /// Removes all items from the . @@ -322,8 +319,14 @@ public void Clear() /// a value that has more complete data than the value you currently have, although their /// comparer functions indicate they are equal. /// - public bool TryGetValue(T equalValue, [MaybeNullWhen(false)] out T actualValue) + public bool TryGetValue(T equalValue, out T? actualValue) { + if (equalValue == null) + { + actualValue = default; + return false; + } + var hashcode = _comparer.GetHashCode(equalValue); // We must capture the _buckets field in a local variable. It is set to a new table on each table resize. @@ -357,6 +360,9 @@ public bool TryGetValue(T equalValue, [MaybeNullWhen(false)] out T actualValue) /// true if an item was removed successfully; otherwise, false. public bool TryRemove(T item) { + if (item == null) + return false; + var hashcode = _comparer.GetHashCode(item); while (true) { @@ -569,9 +575,9 @@ void ICollection.CopyTo(T[] array, int arrayIndex) private void InitializeFromCollection(IEnumerable collection) { - foreach (var item in collection) + foreach (T item in collection) { - AddInternal(item, _comparer.GetHashCode(item), false); + AddInternal(item, false); // , _comparer.GetHashCode(item) } if (_budget == 0) @@ -581,8 +587,12 @@ private void InitializeFromCollection(IEnumerable collection) } } - private bool AddInternal(T item, int hashcode, bool acquireLock) + private bool AddInternal(T item, bool acquireLock) // , int hashcode { + if (item == null) + return false; + + int hashcode = _comparer.GetHashCode(item); while (true) { var tables = _tables; diff --git a/src/ConcurrentHashSet/ConcurrentHashSet.csproj b/src/ConcurrentHashSet/ConcurrentHashSet.csproj index 5c948b8..aa013a5 100644 --- a/src/ConcurrentHashSet/ConcurrentHashSet.csproj +++ b/src/ConcurrentHashSet/ConcurrentHashSet.csproj @@ -1,7 +1,7 @@  - ConcurrentCollections + System.Collections.Concurrent.ConcurrentHashSet Bar Arnon True Copyright 2021 Bar Arnon @@ -12,20 +12,20 @@ True true true - ConcurrentHashSet + System.Collections.Concurrent.ConcurrentHashSet MIT True concurrency true - ConcurrentCollections + System.Collections.Concurrent snupkg - netstandard1.0;netstandard2.0;net461 + netstandard1.0;netstandard2.0;netstandard2.1;net6 ConcurrentHashSet - 1.2.0 + 1.2.1 - + \ No newline at end of file diff --git a/src/ConcurrentHashSet/NullableAttributes.cs b/src/ConcurrentHashSet/NullableAttributes.cs deleted file mode 100644 index ffe6f5f..0000000 --- a/src/ConcurrentHashSet/NullableAttributes.cs +++ /dev/null @@ -1,20 +0,0 @@ -#if !NETSTANDARD2_1 - -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } -} - -#endif \ No newline at end of file