2

1에서 100까지의 병렬화에 대한 100 개의 레코드가 있습니다. 이제 Parallel.For를 사용하여 Parallel에서 다음과 같이 실행할 수 있습니다. 컴퓨팅 자원Parallel.For 루프 - 각 스레드에 대한 고유 한 데이터 엔티티 할당

Parallel.For(0, limit, i => 
    { 
     DoWork(i); 
    }); 

있지만 특정 제한이에, 각 스레드는 동일한 데이터 엔티티와 함께 ​​작동하도록해야합니다 및 데이터 엔티티의 수는 제한이 있습니다 서로 복제 및에 저장하여 고급에서 생성되는, 10 말 사전 또는 목록과 같은 구조. 이제 다음 코드를 사용하여 병렬화의 양을 제한 할 수 있습니다

Parallel.For(0, limit, new ParallelOptions { MaxDegreeOfParallelism = 10 }, i => 
    { 
     DoWork(i); 
    }); 

을하지만 문제는 데이터 개체가 실행의 다른 현재의 thread가 사용되지 않도록, 각 수신 스레드에 대한 고유 데이터 엔티티를 할당하는 방법입니다 왜냐하면 스레드와 데이터 엔티티의 수가 동일하기 때문에 기아가 문제가되지 않기 때문입니다. 생각할 수있는 방법은 각 데이터 엔티티에 대해 부울 값을 작성하여 사용 중인지 여부를 지정하므로 사전 또는 목록을 반복하여 사용 가능한 다음 데이터 엔티티를 찾고 전반적인 할당 프로세스를 잠글 수 있습니다. 하나의 스레드는 주어진 시간에 데이터 엔티티를 할당 받지만이 문제는 훨씬 더 세련된 해결책이 될 것입니다. 제 버전은 해결 방법 일뿐 실제 수정 사항이 아닙니다. 내 논리는 다음과 같습니다

Parallel.For(0, limit, new ParallelOptions { MaxDegreeOfParallelism = 10 }, i => 
     { 
      lock(All_Threads_Common_Object) 
      { 
       Check for available data entity using boolean 
       Assign the Data entity 
      } 
      DoWork(i); 

      Reset the Boolean value for another thread to use it 
     }); 

이 문제는 더 해명

+2

필요한 패턴은 생산자 - 소비자가 수정됩니다. 프로듀서는 처음 엔 10 개의 엔티티를 추가하고 고객은 엔티티를 얻으려고합니다. 엔티티가 10 개있을 때 나머지 스레드는 제작자가 새 항목을 넣길 기다립니다. 소비자 작업이 끝나면 엔티티가 생산자 스택에 다시 저장됩니다. –

+0

@pwas 그것이 생산자 소비자가 아니라 객체 풀링입니다. 어쨌든 Parallel.For를 사용하면 잠금 초기화 또는 풀링없이 각 스레드에 고유 한 객체를 전달하는 스레드 초기화 함수를 지정할 수 있습니다. –

답변

4

concurrent collection을 사용하여 10 개의 개체를 저장할 수 있습니다. 각 Worker는 하나의 데이터 엔티티를 가져 와서 사용하고 돌려줍니다. 시나리오에서 정상적인 것은 스레드로부터 안전하지 않기 때문에 동시 수집을 사용하는 것이 중요합니다. 그래서 같이

:

var queue = new ConcurrentQueue<DataEntity>(); 
// fill the queue with 10 items 

Parallel.For(0, limit, new ParallelOptions { MaxDegreeOfParallelism = 10 }, i => 
    { 
     DataEntity x; 
     if(!queue.TryDequeue(out x)) 
      throw new InvalidOperationException(); 
     DoWork(i, x); 
     queue.Enqueue(x); 
    }); 

또는 요구를 차단하는 것이 제공 될 경우 BlockingCollection에서 일을 마무리.

편집 : 을 수행하지 마십시오.을 반복하여 대기 상태로 유지하십시오. 대신 BlockingCollection을 다음과 같이 사용하십시오.

var entities = new BlockingCollection(new ConcurrentQueue<DataEntity>()); 

// fill the collection with 10 items 

Parallel.For(0, limit, new ParallelOptions { MaxDegreeOfParallelism = 10 }, i => 
    { 
     DataEntity x = entities.Take(); 
     DoWork(i, x); 
     entities.Add(x); 
    }); 
+0

단순한 동시 수집으로는 충분하지 않습니다. 예, 스레드로부터 안전하지만 컬렉션 내의 새 항목을 기다리는 스레드를 만들지 않습니다. –

+0

문제 정의에 따르면 10 명의 근로자와 10 개의 데이터 엔티티가 있습니다. 작업자가 빈 대기열을 만날 수 없어야합니다. –

+0

@pwas에 의해 제안 된 바와 같이 올바른 스레드 안전성은 주요한 문제는 아니지만 스레드가 다음 사용 가능한 데이터 엔터티를 기다리는 것을 주된 문제로합니다. –

5

를 사용하여 스레드 로컬 초기화 기능을 허용 Parallel.For의 과부하를 필요로하는 경우 알려 주시기 바랍니다.

Parallel.For<DataEntity>(0, limit, 
    //will run once for each thread 
    () => GetThreadLocalDataEntity(), 

    //main loop body, will run once per iteration 
    (i, loop, threadDataEntity) => 
    { 
     DoWork(i, threadDataEntity); 
     return threadDataEntity; //we must return it here to adhere to the Func signature. 
    }, 

    //will run once for each thread after the loop 
    (threadDataEntity) => threadDataEntity.Dispose() //if necessary 
); 

당신이 질문을 게시 한 대이 방법의 가장 큰 장점은, DataEntity의 할당이 루프 반복 당 한번도 스레드에 한 번 발생합니다.

+0

데이터 엔티티를 재 할당해야하는 제 시나리오의 경우 GetThreadLocalDataEntity()의 코드를 잠그고 부울을 기반으로 사용할 수있는 데이터 엔터티를 확인해야합니다. 오해하는 경우이를 수정하십시오. 또한 MaxDegreeOfParallelism = 10으로 설정하면 스레드 중 하나가 루프를 완료 할 때까지 11 번째 스레드에 대해 GetThreadLocalDataEntity() 메서드를 호출하지 않겠습니까? –

+1

@MrinalKamboj'GetThreadLocalDataEntity()'가 아직 잠겨 있어야합니다.부울 플래그를 사용할 필요는 없습니다. 예를 들어 사용 가능한 모든 DataEntity 객체를 대기열에 넣고 각 스레드에 대해 객체 하나를 dequeue 할 수 있습니다. 두 번째 질문에 대해서는'MaxDegreeOfParallelism = 10'을 설정하면 11 번째 스레드가 없습니다. – Rotem

+0

사실 내 요구 사항은 아무 것도 반환하지 않기 때문에 Func이없는 옵션이 있습니다. 데이터 엔티티가 루프를 활용하고 완료되었으므로 반환과 아무 관계가 없습니다. –