본문 바로가기
C#

C#] ConcurrentBag

by Fastlane 2023. 1. 11.
728x90
반응형

C#에서는 여러 스레드를 이용하여 동시에 다루어야 하는 objects group을 위한 class가 여러개 있는데, 그 중 하나가 ConcurrentBag<T>이다. 

 

ConcurrentBag으로부터 elements 추가, 접근, 삭제를 하는 방법을 알아보자. 그리고, ConcurrentBag<T>의 장단점도 정리해보자. 

What Is a ConcurrentBag in C#?

generic collection type이다. 

namespace : System.Collection.Generic

objects의 정렬되지 않은 collection이다. (ConcurrentBag에 items을 추가한 순서와, 되찾을떄의 순서가 같지 않다. indexer로 ConcurrentBag의 items에 접근할 수 없다. )

IProducerConsumerCollection<T> interface를 구현한다. 

thread-safe collection이다. 

How to Create a ConcurrentBag in C#

Creating an Empty ConcurrentBag in C#

ConcurrentBag<int> myNumbers = new();

Creating a ConcurrentBag With Initial Items

var myConcurrentBag = new ConcurrentBag<int>() { 2, 4, 6, 8, 10 };

ConcurrentBag은 지정된 용량없이, 수행하는 동작에 따라 사이즈가 증가/감소할 수 있다. 

Creating a ConcurrentBag by Passing Another Collection as an Argument

var myList = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
var myConcurrentBag = new ConcurrentBag<int>(myList);

datatype만 동일하다면 IEnumerable<T>를 전달하여 ConcurrentBag<T>를 생성할 수 있다. 

Working With a ConcurrentBag in a Multithreaded Environment

ConcurrentBag<T>를 사용하여 멀티스레드 환경에서 안전하게 데이터를 처리해보자. 

Adding Elements to a ConcurrentBag Concurrently

public static ConcurrentBag<int> CreateAndAddToConcurrentBagConcurrently()
{
    ConcurrentBag<int> numbersBag = new();
    Parallel.For(0, 1000, i =>
    {
        numbersBag.Add(i);
    });
    return numbersBag;
}

Paraller.For method로 병렬로 loop를 실행할 수 있다. 

Remove Elements From a ConcurrentBag Concurrently

public static ArrayList RemoveFromConcurrentBagConcurrently(ConcurrentBag<int> bag)
{
    var numbersList = new ArrayList();
    Parallel.For(0, 20, i =>
    {
        if (bag.TryTake(out int number))
        {
            Console.WriteLine($"Thread {Environment.CurrentManagedThreadId} took item: {number}");
            numbersList.Add(number);
        }
    });
    return numbersList;
}

TryTake 함수는 ConcurrentBag<T> class 함수이다. 

동시다발성 동작이므로, ConcurrentBag으로부터 item이 삭제되는 순서와 console print 순서는 다르다. 

Access an Element in a ConcurrentBag Concurrently

TryPeek 함수를 사용한다. 삭제없이 ConcurrentBag으로부터 object를 반환한다. 

public static void AccessItemFromAConcurrentBagConcurrently(ConcurrentBag<int> bag)
{
    Parallel.For(0, 50, i =>
    {
        if (bag.TryPeek(out int number))
        {
            Console.WriteLine("Thread {0} peeked item: {1}", Environment.CurrentManagedThreadId, number);
        }
    });
}

ConcurrentBag Class의 다른 함수들

ToArray

ConcurrentBag의 모든 elements를 new array에 복사한다. 

public static int[] ConcurrentBagToArrayMethod(ConcurrentBag<int> bag)
{
    var myArray = new int[bag.Count];
    myArray = bag.ToArray();

    return myArray;
}

CopyTo

존재하는 1차원 배열에 지정된 index부터 복붙한다. 

public static int[] ConcurrentBagCopyToMethod(ConcurrentBag<int> bag)
{
    var someArray = new int[bag.Count];
    bag.CopyTo(someArray, 0);

    return someArray;
}

Clear

ConcurrentBag의 모든 items을 삭제하는 thread-safe 함수이다.

 

언제 ConcurrentBag을 사용하는가?

멀티스레드에서 동시에 collection items를 조작하고, 순서는 상관 없을때 사용한다. 

순서를 고려해야 한다면 ConcurrentQueue 또는 ConcurrentStack을 사용해야 한다. 

 

장점

thread safety를 제공한다. lock-free mechanism을 사용하여 프로그램 퍼포먼스를 향상시킨다. 

비정렬하므로 동적으로 사이트를 조절할 수 있다. 

단점

순서가 중요할때는 사용할 수 없다. 

결론

ConcurrentBag은 멀티스레드에서 items를 저장 회수하기 유용하지만, 순서가 보장되지 않는다. 필요에 따라 알맞는 collection을 선택할 수 있다. 

728x90
반응형

댓글