diff --git a/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagDemo.cs b/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagDemo.cs index 7db71a42bc..3eedaecb6c 100644 --- a/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagDemo.cs +++ b/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagDemo.cs @@ -1,21 +1,24 @@ using System.Collections.Concurrent; -using System.Collections; namespace ConcurrentBagInCSharp { public class ConcurrentBagDemo { - public static ConcurrentBag CreateConcurrentBag() + public static ConcurrentBag CreateEmptyConcurrentBag() { - ConcurrentBag myNumbers = new(); + return new ConcurrentBag(); + } - var myConcurrentBag = new ConcurrentBag() { 2, 4, 6, 8, 10 }; - var myConcurrentBagCount = myConcurrentBag.Count; + public static ConcurrentBag CreateConcurrentBagWithInitialItems() + { + return new ConcurrentBag() { 2, 4, 6, 8, 10 }; + } + public static ConcurrentBag CreateConcurrentBag() + { var myList = new List() { 1, 2, 3, 4, 5, 6, 7 }; - var anotherConcurrentBag = new ConcurrentBag(myList); - return anotherConcurrentBag; + return new ConcurrentBag(myList); } public static ConcurrentBag CreateAndAddToConcurrentBagConcurrently() @@ -30,9 +33,9 @@ public static ConcurrentBag CreateAndAddToConcurrentBagConcurrently() return numbersBag; } - public static ArrayList RemoveFromConcurrentBag(ConcurrentBag numbersBag) + public static List RemoveFromConcurrentBag(ConcurrentBag numbersBag) { - var result = new ArrayList(); + var result = new List(); if (numbersBag.TryTake(out int number)) { result.Add(number); @@ -41,9 +44,9 @@ public static ArrayList RemoveFromConcurrentBag(ConcurrentBag numbersBag) return result; } - public static ArrayList RemoveFromConcurrentBagConcurrently(ConcurrentBag bag) + public static List RemoveFromConcurrentBagConcurrently(ConcurrentBag bag) { - var numbersList = new ArrayList(); + var numbersList = new List(); Parallel.For(0, 20, i => { if (bag.TryTake(out int number)) @@ -56,9 +59,9 @@ public static ArrayList RemoveFromConcurrentBagConcurrently(ConcurrentBag b return numbersList; } - public static ArrayList AccessItemFromAConcurrentBag(ConcurrentBag bag) + public static List AccessItemFromAConcurrentBag(ConcurrentBag bag) { - var result = new ArrayList(); + var result = new List(); if (bag.TryPeek(out int number)) { @@ -79,22 +82,71 @@ public static void AccessItemFromAConcurrentBagConcurrently(ConcurrentBag b }); } - public static int[] ConcurrentBagToArrayMethod(ConcurrentBag bag) + public static List DrainOwnQueue() { - var myArray = new int[bag.Count]; - myArray = bag.ToArray(); + var bag = new ConcurrentBag(); + for (var i = 1; i <= 5; i++) + { + bag.Add(i); + } + + var taken = new List(); + while (bag.TryTake(out var item)) + { + taken.Add(item); + } - return myArray; + return taken; + } + + public static List DrainStolenQueue() + { + var bag = new ConcurrentBag(); + var taken = new List(); + + // Dedicated threads, never Task.Run: the thread pool is free to hand both pieces of + // work to the same thread, and a consumer running on the producer's thread pops its + // own queue last in, first out instead of stealing. + var producer = new Thread(() => + { + for (var i = 0; i < 5; i++) + { + bag.Add(i); + } + }); + producer.Start(); + producer.Join(); + + var consumer = new Thread(() => + { + while (bag.TryTake(out var item)) + { + taken.Add(item); + } + }); + consumer.Start(); + consumer.Join(); + + return taken; + } + + public static int[] ConcurrentBagToArrayMethod(ConcurrentBag bag) + { + return bag.ToArray(); } public static int[] ConcurrentBagCopyToMethod(ConcurrentBag bag) { + // Count and CopyTo are two separate operations. In a multithreaded program another + // thread can add an item between them, and CopyTo then throws ArgumentException + // because the destination is too short. Size the destination from a snapshot, + // or generously, whenever other threads can still write to the bag. var someArray = new int[bag.Count]; bag.CopyTo(someArray, 0); return someArray; } - + public static void ConcurrentBagClearMethod(ConcurrentBag bag) { bag.Clear(); @@ -102,4 +154,4 @@ public static void ConcurrentBagClearMethod(ConcurrentBag bag) Console.WriteLine($"My concurrent bag contains {bag.Count} item."); // My concurrent bag contains 0 item. } } -} \ No newline at end of file +} diff --git a/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp.csproj b/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp.csproj index f02677bf64..dfb40caafc 100644 --- a/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp.csproj +++ b/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net10.0 enable enable diff --git a/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/Program.cs b/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/Program.cs index e9882e00e5..0bc89713ac 100644 --- a/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/Program.cs +++ b/collections-csharp/ConcurrentBagInCSharp/ConcurrentBagInCSharp/Program.cs @@ -1,13 +1,6 @@ -namespace ConcurrentBagInCSharp -{ - internal class Program - { - static void Main(string[] args) - { - var myConcurrentBag = ConcurrentBagDemo.CreateAndAddToConcurrentBagConcurrently(); - var myConcurrentBagCount = myConcurrentBag.Count; - var isMyConcurrentBagEmpty = myConcurrentBag.IsEmpty; - - } - } -} \ No newline at end of file +using ConcurrentBagInCSharp; + +var myConcurrentBag = ConcurrentBagDemo.CreateAndAddToConcurrentBagConcurrently(); + +Console.WriteLine($"Count: {myConcurrentBag.Count}"); +Console.WriteLine($"IsEmpty: {myConcurrentBag.IsEmpty}"); diff --git a/collections-csharp/ConcurrentBagInCSharp/Tests/ConcurrentBagInCSharpTest.cs b/collections-csharp/ConcurrentBagInCSharp/Tests/ConcurrentBagInCSharpTest.cs index f33e3c9807..c8c0846fca 100644 --- a/collections-csharp/ConcurrentBagInCSharp/Tests/ConcurrentBagInCSharpTest.cs +++ b/collections-csharp/ConcurrentBagInCSharp/Tests/ConcurrentBagInCSharpTest.cs @@ -1,5 +1,4 @@ using ConcurrentBagInCSharp; -using System.Collections; using System.Collections.Concurrent; namespace Tests @@ -25,32 +24,32 @@ public void GivenAnEmptyConcurrentBag_WhenAddingToAConcurrentBag_ThenReturnsAPop } [Fact] - public void GivenAConcurrentBag_WhenRemovingFromAConcurrentBag_ThenReturnsAnArrayList() + public void GivenAConcurrentBag_WhenRemovingFromAConcurrentBag_ThenReturnsAList() { var bag = ConcurrentBagDemo.CreateAndAddToConcurrentBagConcurrently(); var result = ConcurrentBagDemo.RemoveFromConcurrentBag(bag); - Assert.IsType(result); + Assert.IsType>(result); Assert.Single(result); } [Fact] - public void GivenAConcurrentBag_WhenRemovingFromAConcurrentBagConcurrently_ThenReturnsAnArrayList() + public void GivenAConcurrentBag_WhenRemovingFromAConcurrentBagConcurrently_ThenReturnsAList() { var bag = ConcurrentBagDemo.CreateAndAddToConcurrentBagConcurrently(); var result = ConcurrentBagDemo.RemoveFromConcurrentBagConcurrently(bag); - Assert.IsType(result); + Assert.IsType>(result); Assert.NotEmpty(result); } [Fact] - public void GivenAConcurrentBag_WhenReadingFromAConcurrentBag_ThenReturnsAnArrayList() + public void GivenAConcurrentBag_WhenReadingFromAConcurrentBag_ThenReturnsAList() { var bag = ConcurrentBagDemo.CreateAndAddToConcurrentBagConcurrently(); var result = ConcurrentBagDemo.AccessItemFromAConcurrentBag(bag); - Assert.IsType(result); + Assert.IsType>(result); Assert.Single(result); } @@ -64,6 +63,33 @@ public void GivenAConcurrentBag_WhenConvertingToAnArray_ThenReturnsAnArray() Assert.NotEmpty(result); } + [Fact] + public void GivenAConcurrentBagFilledOnOneThread_WhenDrainingOnThatThread_ThenReturnsItemsLastInFirstOut() + { + var result = ConcurrentBagDemo.DrainOwnQueue(); + + Assert.Equal(new List { 5, 4, 3, 2, 1 }, result); + } + + [Fact] + public void GivenAConcurrentBagFilledOnOneThread_WhenDrainingOnADifferentThread_ThenReturnsItemsOldestFirst() + { + var result = ConcurrentBagDemo.DrainStolenQueue(); + + Assert.Equal(new List { 0, 1, 2, 3, 4 }, result); + } + + [Fact] + public void GivenAConcurrentBag_WhenClearingAConcurrentBag_ThenReturnsAnEmptyConcurrentBag() + { + var bag = ConcurrentBagDemo.CreateAndAddToConcurrentBagConcurrently(); + + ConcurrentBagDemo.ConcurrentBagClearMethod(bag); + + Assert.True(bag.IsEmpty); + Assert.Empty(bag); + } + [Fact] public void GivenAConcurrentBag_WhenCopyingToAnArray_ThenReturnsAnArray() { diff --git a/collections-csharp/ConcurrentBagInCSharp/Tests/Tests.csproj b/collections-csharp/ConcurrentBagInCSharp/Tests/Tests.csproj index ebcbe28532..6084a8665e 100644 --- a/collections-csharp/ConcurrentBagInCSharp/Tests/Tests.csproj +++ b/collections-csharp/ConcurrentBagInCSharp/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net10.0 enable enable @@ -9,13 +9,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all