신석현님께서 아주 좋은 지적을 해주셨습니다. 제가 예제로 사용한 코드를 돌리면 매번 prime count가 다르게 나온다는 것이었습니다. 참고를 위해서~~ 다시 그 문제의 코드를 붙여 보겠습니당. :)
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { class Program { static IEnumerable<long> GetPrimeNumber(long num) { List<long> primeList = new List<long>(); Parallel.For(0, num + 1, (i) => { bool isPrime = true; Parallel.For(2, i, (j, loopState) => { if (i % j == 0) { isPrime = false; loopState.Break(); } }); if (isPrime) { primeList.Add(i); } }); return primeList; } static void Main(string[] args) { Stopwatch sw = new Stopwatch(); sw.Start(); IEnumerable<long> primeList = GetPrimeNumber(99999); sw.Stop(); Console.WriteLine("Elapsed : {0}, Found prime counts : {1}", sw.Elapsed.ToString(), primeList.Count()); //뭔가 한다. } } } |
이 코드에서 문제가 되는 곳은 바로~!!!!! List<T>를 사용했다는 점입니다. Parallel.For를 돌리면, 여러 스레드가 구역을 나눠서 동시에 Parallel.For안의 코드를 돌리게 되는데요. 이 과정에서 스레드 간의 충돌이 생겨서 그렇습니다. 문제를 찾아보기 위해서 리플렉터로 List<T>.Add(T)의 코드를 찾아보니...
public void Add(T item) { if (this._size == this._items.Length) { this.EnsureCapacity(this._size + 1); } this._items[this._size++] = item; this._version++; } |
이렇게 되어 있습니다. 여기서 7번째 줄의 'this._size++'부분이 문제가 되는 것 같습니다. 제가 11번째 포스팅에서 적은 내용 처럼 말이죠. 그래서 size가 1증가 되기 전에 같은 같은 인덱스에 값이 두번 쓰여지는 것이 아닌가 하는 추측이 가네욤!!! :)
그래서 가설을 확인해보기 위해서 List<T>를 여러스레드가 동시에 사용가능한 ConcurrentBag<T>로 바꿔서 테스트를 진행해봤습니다. 그랬더니!! 매번 같은 prime count가 나오는 것을 볼 수 있었습니다. 하하하하하 ㅠ_ㅠ;; 참고로 ConcurrentBag<T>.Add<T>의 코드를 봤더니 내부적으로...
private void AddInternal(ThreadLocalList<T> list, T item) { bool lockTaken = false; try { Interlocked.Exchange(ref list.m_currentOp, 1); if ((list.Count < 2) || this.m_needSync) { list.m_currentOp = 0; Monitor.Enter(list, ref lockTaken); } list.Add(item, lockTaken); } finally { list.m_currentOp = 0; if (lockTaken) { Monitor.Exit(list); } } } |
위와 같은 코드를 사용하더군요. Monitor를 사용해서 동기화를 시키고 있죠~? 하하하 :) 날카로운 질문을 해주신 신석현님께 감사드립니다 :)
'C# Parallel Programming' 카테고리의 다른 글
Welcome to Parallel C#(15) - 브레이크와 멈춤의 상관관계. (2) | 2012.02.15 |
---|---|
Welcome to Parallel C#(14.2) - 긴급 패치 2. (2) | 2012.02.14 |
Welcome to Parallel C#(14) - 거기까지. (6) | 2012.02.13 |
Welcome to Parallel C#(13) - 더 편하겡... 더 빠르겡... (0) | 2012.02.10 |
Welcome to Parallel C#(12) - 귀찮으면 안 해. (0) | 2012.02.08 |