Skip to content

Instantly share code, notes, and snippets.

@gekka
Created August 3, 2022 12:16
Show Gist options
  • Save gekka/36c273a9d4ac01d78cc2f8f67925aa1b to your computer and use it in GitHub Desktop.
Save gekka/36c273a9d4ac01d78cc2f8f67925aa1b to your computer and use it in GitHub Desktop.
namespace ConsoleApp1
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
class Program
{
static void Main(string[] args)
{
//new Test<MyClassA, MyClassB>().Sample();
ConcurrentBagTest();
NormalEnumerableTest();
}
static void ConcurrentBagTest()
{
Console.WriteLine(nameof(ConcurrentBagTest));
ConcurrentBag<int> cbag = new ConcurrentBag<int>(Enumerable.Range(1, 10));
var t1 = Task.Run(() =>
{
System.Threading.Thread.Sleep(100);
System.Diagnostics.Debug.WriteLine("WAKE 1");
// Task2の列挙の最中に削除したらどうなるか
while (cbag.Count > 0)
{
cbag.TryTake(out var result);
}
// Task2の列挙の最中に追加したらどうなるか
cbag.AddRange(Enumerable.Range(11, 100));
});
foreach (int i in cbag)
{
if (i == 5)
{
t1.Wait(); //列挙途中に別スレッドで増減させてみる
}
Console.WriteLine($"Value={i} , Count={cbag.Count}");
}
//結果
// ConcurrentBag<T>は列挙の開始時点の状態に対して列挙が行われる。
// 列挙の最中に追加・削除が行われても列挙に影響を受けない
}
static void NormalEnumerableTest()
{
Console.WriteLine(nameof(NormalEnumerableTest));
List<int> list = new List<int>(Enumerable.Range(1, 10));
var t1 = Task.Run(() =>
{
System.Threading.Thread.Sleep(100);
System.Diagnostics.Debug.WriteLine("WAKE 2");
// Task2の列挙の最中に削除したらどうなるか
list.Clear();
// Task2の列挙の最中に追加したらどうなるか
list.AddRange(Enumerable.Range(11, 100));
});
try
{
foreach (int i in list)
{
if (i == 5)
{
t1.Wait(); //列挙途中に別スレッドで増減させてみる
}
Console.WriteLine($"Value={i} , Count={list.Count}");
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.Message);
Console.ResetColor();
}
}
}
/////////////////////////////////////////////////////////
class Test
{
List<MyClassA> list1 = new List<MyClassA>();
List<MyClassB> list2 = new List<MyClassB>();
public void Sample()
{
AddDummy(list1, 100);
AddDummy(list2, 100);
ConcurrentBag<MyClassB> cbug = new ConcurrentBag<MyClassB>();
var result = Parallel.ForEach(list1, (MyClassA data) =>
{
if (!IsAny(data, cbug))
{
// list2はParallel.ForEachの間は増減しないので
// このie2の寿命の間は増減しない
// ゆえに、このWhrereはConcurrentを処理せずとも良い
IEnumerable<MyClassB> ie2 = list2.Where(a => a.Member == data.Member);
cbug.AddRange(ie2); //ConcurrentBagに追加しても他の列挙には影響を与えない
}
});
}
private bool IsAny(MyClassA data)
{
// list2はParallel.ForEachの間は増減しないので
// こちらでも問題はない。
return list2.Any(a => a.Member == data.Member);
}
private bool IsAny(MyClassA data, ConcurrentBag<MyClassB> cbug)
{
// 列挙の最中に増減しても、Anyは列挙開始した時点の内容で処理される
return cbug.Any(a => a.Member == data.Member);
}
public void AddDummy<T>(List<T> list, int count) where T : ConsoleApp21.MyClassA, new()
{
list.AddRange(Enumerable.Range(1, count).Select(i => new T() { Member = i.ToString() }));
}
}
class MyClassA
{
public string Member { get; set; }
public override string ToString() => Member;
}
class MyClassB : MyClassA { }
static class Extension
{
public static void AddRange<T>(this IProducerConsumerCollection<T> list, IEnumerable<T> source)
{
foreach (T item in source)
{
while (!list.TryAdd(item))
{
}
}
}
public static void AddRange<T>(this IProducerConsumerCollection<T> list, IEnumerable<T> source, System.Threading.CancellationToken token)
{
foreach (T item in source)
{
while (!list.TryAdd(item))
{
if (token.IsCancellationRequested)
{
return;
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment