C\#동시 다발 실전 기록 의 Parallel.ForEach 사용
6848 단어 c#parallel.foreach
최근 고객 에 게 식비 계산 시스템 을 개발 해 2000 명의 식 사 를 계산 해 야 한다.수 요 는 직원 의 예약 신고 계획 에 따라 소비 기록 을 검사 하 는 것 이다.만약 에 신고 하지 않 은 음식 이 카드 를 사용 하거나 신고 하지 않 은 것 이 있 으 면 일정한 금액 공제 등 일련의 규칙 을 실시 해 야 한다.처음에는 제 생각 이 쉬 웠 습 니 다.바로 하나의 for 순환 으로 해 결 했 습 니 다.통계 결 과 는 괜 찮 았 지만 계산 이 너무 느 려 서 7,8 분 이 걸 렸 습 니 다.이런 시스템 서 비 스 는 시간 초과 보고 가 잘못 되 어 좀 불쾌 하 다.시간 도 많 지 않 아 먼저 사용자 에 게 제출 하여 사용 하 게 되 었 고,뒤의 논리 가 또 증가 하여 계산 시간 이 길 어 졌 으 며,전체 계산 을 한 번 하 는 데 10 분 가까이 걸 렸 다.이것 은 사용자 에 게 받 아들 일 수 있 는 것 입 니 다.
다 중 스 레 드 하면 가장 먼저 떠 오 르 는 것 은 Task 입 니 다.왜냐하면.net 4.0 이상 Task 는 좋 은 방법 을 많이 포 장 했 기 때 문 입 니 다.그러나 Task 는 스 레 드 를 많이 열 어 임 무 를 수행 하고 마지막 으로 결 과 를 통합 하면 빠 를 수 있 지만 더 빠 르 고 싶 어서 다른 대상 이 생각 났 습 니 다.Parallel.이전에 유지보수 코드 는 다른 사람 이 쓴 Parallel.Invoke 를 만난 적 이 있 습 니 다.이 함수 의 역할 은 여러 가지 임 무 를 동시에 수행 하 는 것 이 라 고 지 정 했 을 뿐 입 니 다.만약 에 여러 시간 이 걸 리 는 작업 을 만나면 그들 사이 에 변 수 를 기여 하지 않 는 방법 이 좋 습 니 다.제 상황 은 하나의 집합 을 동시에 실행 하려 고 하기 때문에 List.ForAll 이라는 방법 을 사 용 했 습 니 다.사실은 확장 방법 입 니 다.완전한 호출 은 List.As Parallel().ForAll 입 니 다.먼저 병행 을 지원 하 는 집합 으로 전환 해 야 합 니 다.Parallel.Foreach 와 같 습 니 다.집합 안의 요 소 를 동시에 수행 하 는 것 이 목적 입 니 다.
그래서 원래 의 foreach 를 List.As Parallel().ForAll 로 바 꾸 었 습 니 다.실행 하기 시 작 했 습 니 다.과연 속도 가 놀 라 웠 습 니 다.2 분 도 안 되 어 결 과 를 삽입 하 였 으 나 마지막 으로 메 인 키 가 중복 되 는 오류 입 니 다.이 오 류 는 병발 을 사 용 했 기 때문에 이 때 변 수 는 증가 하 는 것 입 니 다.사실은 여러 스 레 드 가 동시에 id 값 을 얻 었 기 때 문 입 니 다.모두 증가 한 후에 반복 되 었 습 니 다.예 를 들 어 다음 과 같 습 니 다.
int num = 1;
List<int> list = new List<int>();
for (int i = 1; i <= 2000; i++)
{
list.Add(i);
}
Console.WriteLine($"num :" + num.ToString());
list.AsParallel().ForAll(n =>
{
num++;
});
Console.WriteLine($" , {list.Count} :" + num.ToString());
Console.ReadKey();
이 코드 는 하나의 변 수 를 2000 번 증가 시 키 는 것 으로 정상 적 인 결 과 는 2001 이 어야 하지만 실제 결 과 는 다음 과 같다.경험 이 있 는 학생 들 은 바로 자 물 쇠 를 추가 해 야 한 다 는 것 을 생각 할 수 있 습 니 다.C\#많은 자물쇠 대상 이 내장 되 어 있 습 니 다.예 를 들 어 lock 상호 배척 자물쇠,Interlocked 내부 자물쇠,Monitor 등 몇 가지 흔히 볼 수 있 습 니 다.lock 내부 실현 은 사실은 Monitor 대상 을 사용 한 것 입 니 다.변수 자체 증가,Interlocked 대상 에 게'변수 자체 증가,자체 감소,또는 추가 등 방법'을 제공 합 니 다.저 희 는 자체 증가 방법 인 Interlocked.Increment 를 사용 합 니 다.함 수 는 int Increment(ref int num)로 정의 합 니 다.이 대상 은 원자 적 인 변 수 를 제공 하여 자체 증가 작업 을 하고 목표 수 치 를 전달 하 며 되 돌아 오 거나 ref num 은 모두 증가 한 결과 입 니 다.이전의 기초 위 에서 우 리 는 약간의 코드 를 추가 했다.
num = 1;
Console.WriteLine($"num :" + num.ToString());
list.AsParallel().ForAll(n =>
{
Interlocked.Increment(ref num);
});
Console.WriteLine($" , {list.Count} :" + num.ToString());
Console.ReadKey();
실행 결 과 를 살 펴 보 겠 습 니 다.자 물 쇠 를 추가 한 후에 ID 중복 은 해 결 된 셈 입 니 다.사실 너무 일찍 기뻐 하지 마 세 요.정상 적 인 환경 에 ID 가 있 기 때문에 우 리 는 이런 ID 로 대상 을 구축 할 수 있 습 니 다.그래서 코드 를 썼 습 니 다.집합 으로 이런 ID 를 추 가 했 습 니 다.더욱 진실 한 모 의 생산 환경 을 위해 저 는 forAll 에 순환 코드 를 다음 과 같이 추 가 했 습 니 다.
num = 1;
Random random = new Random();
var total = 0;
var m = new ConcurrentBag<int>();
list.AsParallel().ForAll(n =>
{
var c = random.Next(1, 50);
Interlocked.Add(ref total, c);
for (int i = 0; i < c; i++)
{
Interlocked.Increment(ref num);
m.Add(num);
}
});
Console.WriteLine($" , + {list.Count} :" + num.ToString());
Console.WriteLine($" :{total + 1}");
var l = m.GroupBy(n => n).Where(o => o.Count() > 1);
Console.WriteLine($" ConcurrentBag num, :{l.Count()} ");
Console.ReadKey();
위의 코드 에서 저 는 스 레 드 안전 집합 ConcurrentBag
num = 1;
total = 0;
using (var q = new BlockingCollection<int>())
{
list.AsParallel().ForAll(n =>
{
var c = random.Next(1, 50);
Interlocked.Add(ref total, c);
for (int i = 0; i < c; i++)
{
// Task.Delay(100);
q.Add(Interlocked.Increment(ref num));
//
//lock (objLock)
//{
// num++;
// q.Add(num);
//}
}
});
q.CompleteAdding();
Console.WriteLine($"num :{total}, :{num}");
var x = q.GroupBy(n => n).Where(o => o.Count() > 1);
Console.WriteLine($" BlockingCollection+Interlocked num, :{x.Count()} ");
Console.ReadKey();
}
여기 서 저 는 다른 스 레 드 의 안전 한 집합 Blocking Collection 을 테스트 했 습 니 다.이 집합 에 대한 사용 은 MSDN 문 서 를 직접 찾 으 십시오.위의 관건 적 인 코드 는 안전 집합 의 반환 값 을 직접 추가 하면 집합 이 중복 되 지 않도록 보장 할 수 있 습 니 다.그러나 사실은 아래 의 lock 은 정식 환경 에 더욱 적 용 됩 니 다.왜냐하면 저희 가 추가 한 것 은 대상 이 기본 적 인 유형 수치 가 아니 기 때 문 입 니 다.실행 결 과 는 다음 과 같 습 니 다.이로써 우리 의 문 제 는 해결 되 었 고 계산 시간 이 원래 의 9 분 여 에서 110 초 정도 로 떨 어 졌 다.이 를 통 해 알 수 있 듯 이 Parallel 의 처 리 는 매우 기 똥 차 고 유일 하 게 부족 한 것 은 CPU 를 차지 하 며 계산 을 실행 한 후에 CPU 는 88%에 달 했다.계산 결과 첨부:
전후 대비 최적화
요약:
C\#안전 집합 이 병발 하 는 상황 에서 반드시 안전 한 것 이 아니 라 실제 응용 장면 과 검증 결 과 를 결합 시 켜 야 한다.Parallel.Foreach 는 순환 수량 이 상당 한 상황 에서 사용 할 수 있 습 니 다.공유 변수 가 있 으 면 자물쇠 와 함께 동기 화 처리 해 야 합 니 다.이 방법 을 신중하게 사용 해 야 합 니 다.만약 에 방법 내부 에 데이터 베 이 스 를 조작 하 는 기억 이 있 으 면 사무 처 리 를 늘 려 야 합 니 다.그렇지 않 으 면 하하 합 니 다.
자,이상 이 이 글 의 모든 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가 치 를 가지 기 를 바 랍 니 다.여러분 의 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
C#Task를 사용하여 비동기식 작업을 수행하는 방법라인이 완성된 후에 이 라인을 다시 시작할 수 없습니다.반대로 조인(Join)만 결합할 수 있습니다 (프로세스가 현재 라인을 막습니다). 임무는 조합할 수 있는 것이다. 연장을 사용하여 그것들을 한데 연결시키는 것이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.