문제

본체 내부에서 집중적 인 작동을 실행하는 oreareach 루프가 있습니다.

작업은 해시 가능을 사용하여 값을 저장할 수 있으며 다른 연속 루프 항목에 대해 재사용 할 수 있습니다. 집중 작업이 완료된 후 해시 테이블에 추가하면 다음 루프 항목이 집중 작업을 다시 실행하는 대신 해시 테이블을 찾아 객체를 재사용 할 수 있습니다.

그러나 Paremal.Fereach를 사용하고 있기 때문에 안전하지 않은 문제가 있으므로 Hashtable.Add를 유발하고 ContainSKey (키) 호출은 병렬로 실행될 수 있으므로 동기화되지 않습니다. 잠금 장치를 도입하면 성능 문제가 발생할 수 있습니다.

샘플 코드는 다음과 같습니다.

Hashtable myTable = new Hashtable;
Parallel.ForEach(items, (item, loopState) =>
{
    // If exists in myTable use it, else add to hashtable
    if(myTable.ContainsKey(item.Key))
    {
       myObj = myTable[item.Key];
    }
    else
    {
       myObj = SomeIntensiveOperation();
       myTable.Add(item.Key, myObj); // Issue is here : breaks with exc during runtime
    }
    // Do something with myObj
    // some code here
}

이 시나리오를 처리 할 수있는 TPL 라이브러리 내부에는 API, 속성 설정이 있어야합니다. 거기가 있습니까?

도움이 되었습니까?

해결책

당신은 찾고 있습니다 System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>. 새로운 동시 컬렉션은 크게 향상된 잠금 장치를 사용하고 병렬 알고리즘으로 우수하게 수행해야합니다.

편집 : 결과는 다음과 같습니다.

ConcurrentDictionary<T,K> cache = ...;
Parallel.ForEach(items, (item, loopState) =>
{
    K value;
    if (!cache.TryGetValue(item.Key, out value))
    {
        value = SomeIntensiveOperation();
        cache.TryAdd(item.Key, value);
    }

    // Do something with value
} );

경고의 말 : 요소가 있다면 items 모두 독특하지는 않습니다 item.Key, 그 다음에 SomeIntensiveOperation 그 키를 위해 두 번 호출 될 수 있습니다. 예에서는 키가 전달되지 않습니다 SomeIntensiveOperation, 그러나 "값으로 무언가를하는"코드가 키/valuea 및 key/valueb 쌍을 실행할 수 있으며, 한 가지 결과 만 캐시에 저장 될 수 있음을 의미합니다 (반드시 중단으로 계산 된 첫 번째 결과는 아닙니다). 이것을 처리하려면 평행 게으른 공장이 필요합니다 만약에 문제입니다. 또한 명백한 이유로 약간의 집중화는 스레드 안전해야합니다.

다른 팁

~을 체크하다 System.collections.concurrent 네임 스페이스 당신이 필요하다고 생각합니다 동시 소설

ReaderWriterLock을 사용하십시오. 이것은 짧은 기간 동안 많은 읽기와 글을 많이 가진 작업에 좋은 성능을 가지고 있습니다. 귀하의 문제는이 사양에 맞는 것 같습니다.

모든 읽기 작업은 빠르게 실행되고 잠글 수 있습니다. 누군가가 차단할 때는 쓰기가 발생할 때 뿐이며, 그 글은 해시 가능에 무언가를 밀어내는 데 걸리는 한 글입니다.

MSDN의 readerWriterLockSlim

코드를 버릴 것 같아요 ...

ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
Hashtable myTable = new Hashtable();
Parallel.ForEach(items, (item, loopState) =>
{
    cacheLock.EnterReadLock();
    MyObject myObj = myTable.TryGet(item.Key);
    cacheLock.ExitReadLock();

    // If the object isn't cached, calculate it and cache it
    if(myObj == null)
    {
       myObj = SomeIntensiveOperation();
       cacheLock.EnterWriteLock();
       try
       {
           myTable.Add(item.Key, myObj);
       }
       finally
       {
           cacheLock.ExitWriteLock();
       }           
    }
    // Do something with myObj
    // some code here
}

static object TryGet(this Hashtable table, object key)
{
    if(table.Contains(key))
        return table[key]
    else
        return null;
}

나는 (다소 명백한) 잠금을 사용하는 것 외에 다른 올바른 선택을 보지 못합니다 (동기화 된 해시 테이블은 모든 메소드를 잠금으로 무시합니다).

또 다른 옵션은 사전이 동기화되지 않도록하는 것입니다. 레이스 조건은 사전을 손상시키지 않으며, 단지 불필요한 계산을 수행하기 위해 코드 만 있으면됩니다. 자물쇠 또는 누락 된 메모 화가 더 나쁜 영향을 미치는지 확인하기 위해 코드를 프로파일 링하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top