뮤텍스와 크리티컬 섹션의 차이점은 무엇입니까?
Linux, Windows의 관점에서 설명해 주세요.
C#에서 프로그래밍을 하고 있는데, 이 두 용어가 다를까요?가능한 한, 예를 들면…을 투고해 주세요.
감사해요.
Windows 의 경우, 중요 섹션은 뮤텍스보다 가볍습니다.
뮤텍스는 프로세스 간에 공유할 수 있지만 항상 오버헤드가 있는 커널에 대한 시스템 호출이 발생합니다.
중요한 섹션은 1개의 프로세스 내에서만 사용할 수 있지만 경합이 발생할 경우에만 커널 모드로 전환된다는 장점이 있습니다.일반적인 경우인 Uncontended acquire는 매우 빠릅니다.경합의 경우 이벤트나 세마포어 등의 동기화 프리미티브에서 대기하기 위해 커널로 들어갑니다.
두 사람의 시간을 비교한 간단한 샘플 앱을 만들었습니다.내 시스템에서 100만 명의 무차별 취득 및 릴리스는 뮤텍스가 1초 동안 걸립니다.중요한 섹션은 1,000,000을 획득할 때 최대 50밀리초 걸립니다.
여기 테스트 코드가 있습니다. 뮤텍스가 1차 또는 2차일 경우 비슷한 결과가 나왔기 때문에 다른 효과는 나타나지 않습니다.
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;
// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
}
QueryPerformanceCounter(&end);
int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
}
QueryPerformanceCounter(&end);
int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
printf("Mutex: %d CritSec: %d\n", totalTime, totalTimeCS);
이론적으로 중요한 섹션은 코드가 공유 리소스에 액세스하기 때문에 동시에 여러 스레드에 의해 실행되어서는 안 되는 코드입니다.
뮤텍스는 중요한 섹션을 보호하기 위해 사용되는 알고리즘(및 경우에 따라서는 데이터 구조의 이름)입니다.
실제로 윈도우에서 사용할 수 있는 뮤텍스 구현은 많이 있습니다.이들은 주로 잠금 수준, 범위, 비용 및 다양한 경합 수준에서의 성능에 따라 구현 결과에 따라 다르다.다양한 뮤텍스 구현 비용 차트는 CLR Inside Out - 확장성을 위한 동시성 사용을 참조하십시오.
사용 가능한 동기화 프리미티브.
lock(object)
스테이트먼트는 참조용으로 MSDN을 사용하여 구현됩니다.
최근 몇 년 동안 논블로킹 동기화에 대한 많은 연구가 이루어졌습니다.목표는 잠금 또는 대기 없이 알고리즘을 구현하는 것입니다.이러한 알고리즘에서 프로세스는 다른 프로세스가 작업을 완료하도록 지원하므로 프로세스가 최종적으로 작업을 완료할 수 있습니다.그 결과 어떤 작업을 수행하려고 했던 다른 프로세스가 정지된 경우에도 프로세스가 작업을 완료할 수 있습니다.Usinig 잠금을 사용하면 잠금을 해제하지 않고 다른 프로세스를 계속할 수 없게 됩니다.
다른 답변과 더불어 Windows의 중요한 섹션에 대한 자세한 내용은 다음과 같습니다.
- 한
InterlockedCompareExchange
- 중요 단면 구조에는 뮤텍스를 위한 공간이 있습니다.처음에는 할당되지 않았습니다.
- 중요한 섹션의 스레드 간에 경합이 있을 경우 뮤텍스가 할당되어 사용됩니다.임계 섹션의 성능이 뮤텍스 성능으로 저하됩니다.
- 높은 경합이 예상되는 경우 스핀 카운트를 지정하는 임계 섹션을 할당할 수 있습니다.
- 스핀 카운트의 크리티컬섹션에 경합이 있는 경우, 크리티컬섹션을 취득하려고 하는 스레드는 프로세서 사이클의 수만큼 회전합니다(스위치 대기).다른 스레드로 컨텍스트 전환을 실행하는 사이클 수가 소유 스레드가 뮤텍스를 해제하는 데 걸리는 사이클 수보다 훨씬 많을 수 있으므로 sleep보다 성능이 향상될 수 있습니다.
- 스핀 카운트가 만료되면 뮤텍스가 할당됩니다.
- 소유 스레드가 크리티컬섹션을 해방할 때 뮤텍스가 할당되어 있는지 여부를 확인해야 합니다.연결되어 있는 경우 뮤텍스가 대기 스레드를 해방하도록 설정됩니다.
Linux 에서는 스핀 카운트의 크리티컬 섹션과 같은 목적을 가진 스핀 록이 있다고 생각합니다.
Critical Section 및 Mutex는 운영체제 고유의 것이 아니며 멀티스레딩/멀티프로세싱의 개념입니다.
Critical Section(중요한 섹션)은 임의의 시간에 스스로만 실행할 수 있는 코드입니다(예를 들어 5개의 스레드가 동시에 실행되고 어레이를 갱신하는 "critical_section_function"이라는 함수가 있습니다).5개의 스레드 모두 어레이를 동시에 업데이트하지 않도록 해야 합니다.따라서 프로그램이 critical_section_function()을 실행하고 있을 때 다른 스레드는 critical_section_function()을 실행해서는 안 됩니다.
mutex* Mutex는 중요한 섹션 코드를 구현하는 방법입니다(토큰이라고 생각하시면 됩니다).critical_section_code를 실행하려면 스레드에 소유권이 있어야 합니다.)
뮤텍스는 스레드가 취득할 수 있는 오브젝트이며, 다른 스레드가 취득할 수 없습니다.필수 사항이 아닌 권고 사항입니다. 스레드는 mutex가 나타내는 리소스를 취득하지 않고도 사용할 수 있습니다.
Critical 섹션은 운영체제에 의해 간섭되지 않음을 보증하는 코드 길이입니다.의사 코드에서는 다음과 같습니다.
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
Linux에서 중요한 선택과 동일한 '빠른' 윈도우는 futex가 될 것입니다. fast user space mutex는 빠른 사용자 공간 mutex를 나타냅니다.futex와 mutex의 차이점은 futex의 경우 커널은 조정이 필요할 때만 관여하기 때문에 원자 카운터가 변경될 때마다 커널과 통신하는 오버헤드를 줄일 수 있다는 것입니다.이를 통해 일부 응용 프로그램에서 잠금을 협상하는 시간을 크게 절약할 수 있습니다.
뮤텍스를 공유하기 위해 사용하는 수단을 사용하여 프로세스 간에 futex를 공유할 수도 있습니다.
안타깝게도 futex는 구현하기가 매우 까다로울 수 있습니다(PDF). (2018년 업데이트, 2009년 업데이트만큼 무섭지 않습니다.)
그 외에는 두 플랫폼 모두 거의 동일합니다.(바람직하게) 굶주림을 초래하지 않는 방식으로 공유 구조에 원자적인 토큰 기반 업데이트를 수행합니다.남은 것은 단순히 그것을 성취하는 방법이다.
Windows에서 중요한 섹션은 프로세스에 대해 로컬입니다.뮤텍스는 프로세스 간에 공유/액세스할 수 있습니다.기본적으로 중요한 부분은 훨씬 저렴합니다.Linux에 대해서는 구체적으로 언급할 수 없지만, 일부 시스템에서는 동일한 내용의 별칭일 뿐입니다.
2센트를 더하면 중요한 섹션이 구조체로 정의되고 사용자 모드 컨텍스트에서 작업이 수행됩니다.
ntdll!_RTL_CRITIAL_섹션+0x000 DebugInfo : Ptr32 _ RTL _ CRITIAL _ Section _ DEBUG 。+0x004 LockCount : Int4B+0x008 재귀수 : Int4B+0x00c Owning Thread : Ptr32 보이드+0x010 LockSemaphore : Ptr32 보이드+0x014 SpinCount : Uint4b
반면 mutex는 Windows 개체 디렉토리에서 작성된 커널 개체(ExMutantObjectType)입니다.뮤텍스 동작은 대부분 커널 모드로 구현됩니다.예를 들어, Mutex를 작성할 때 nt를 호출하게 됩니다.커널의 NtCreateMutant.
마이클의 훌륭한 답변입니다.C++11에서 도입된 뮤텍스 클래스의 세 번째 테스트를 추가했습니다.그 결과는 다소 흥미롭고, 여전히 단일 프로세스에 대한 CRITIAL_SECTION 객체에 대한 그의 원래 지지를 뒷받침합니다.
mutex m;
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;
// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
}
QueryPerformanceCounter(&end);
int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
}
QueryPerformanceCounter(&end);
int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
m.lock();
m.unlock();
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
m.lock();
m.unlock();
}
QueryPerformanceCounter(&end);
int totalTimeM = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
printf("C++ Mutex: %d Mutex: %d CritSec: %d\n", totalTimeM, totalTime, totalTimeCS);
결과는 217, 473, 19였습니다(지난 두 번의 횟수는 Michael의 횟수와 거의 비슷하지만, 제 기계는 Michael의 것보다 적어도 4년은 젊기 때문에 XPS-8700이 나온 2009년에서 2013년 사이에 속도가 빨라진 것을 알 수 있습니다).새로운 뮤텍스 클래스는 Windows 뮤텍스보다 속도는 2배 빠르지만 Windows CRITIAL_SECTION 객체의 속도는 10분의 1 미만입니다.비재귀 뮤텍스만 테스트했습니다.CRITIAL_SECTION 객체는 재귀적입니다(같은 횟수를 유지하면 1개의 스레드가 반복적으로 입력할 수 있습니다).
여러 스레드에 의해 코드 섹션이 입력되지 않도록 중요한 섹션이 보호된다는 설명을 발견했습니다.코드는 읽기 전용이며 여러 스레드로 수정할 수 없으므로 코드를 보호하는 것은 의미가 없습니다.일반적으로 필요한 것은 데이터가 여러 스레드에 의해 수정되어 일관성이 없는 상태가 되지 않도록 보호하는 것입니다.일반적으로 뮤텍스(또는 같은 목적을 충족하는 크리티컬 섹션)는 일부 데이터와 관련지어져야 합니다.이 데이터에 액세스하는 각 코드 섹션은 뮤텍스/크리티컬 섹션을 채우고 데이터 액세스가 완료되면 해제해야 합니다.이것은 단순히 함수에 들어가는 나사산을 잠그는 것보다 훨씬 더 미세한 입자가 될 수 있습니다.또, 제 경험상, 어떤 동기화에 의한 잠금 기능은, 에러, 특히 데드 록에 대해서 훨씬 더 잘 대응하고 있습니다.이 주제를 다루는 좋은 기사는 https://www.bogotobogo.com/cplusplus/multithreaded4_cplusplus11B.php에서 찾을 수 있습니다.
즉, 요약(재귀적) 뮤텍스와 중요 섹션은 기본적으로 동일한 목적을 충족합니다. 즉, 코드를 보호하는 것이 아니라 데이터를 보호하는 것입니다.
중요한 섹션은 일반 커널 뮤텍스보다 더 효율적으로 구현될 수 있습니다.첫 번째 답변의 예는 동기화 프리미티브의 용도를 나타내지 않기 때문에 약간 오해의 소지가 있습니다.여러 스레드로부터의 액세스 동기화입니다.이 예에서는 크리티컬섹션/뮤텍스가 다른 스레드에 의해 소유되지 않을 때 사소한 케이스만 측정합니다.예를 들어 두 개의 스레드가 인터록된 짧은 기간에 데이터에 액세스하면 중요한 섹션의 효율성이 향상되지만 동일한 데이터에 액세스하는 스레드가 많으면 효율성이 저하될 수 있습니다.각 스레드는 중요한 섹션 구현의 일부인 세마포를 포기하고 대기할 때까지 스핀록됩니다.실행 시간을 측정할 때도 이러한 경우를 고려해야 합니다.
C 함수는 실제 파라미터만 사용하는 경우 재진입이라고 불립니다.
재진입 함수는 동시에 여러 스레드로 호출할 수 있습니다.
재진입 기능의 예:
int reentrant_function (int a, int b)
{
int c;
c = a + b;
return c;
}
비재진입 함수의 예:
int result;
void non_reentrant_function (int a, int b)
{
int c;
c = a + b;
result = c;
}
라이브러리 Cstrtok()
는 재진입할 수 없으며 동시에 2개 이상의 스레드에서 사용할 수 없습니다.
SDK에는 버전의 SDK가 되어 있습니다.strtok()
라고 하는strtok_r()
;
언급URL : https://stackoverflow.com/questions/800383/what-is-the-difference-between-mutex-and-critical-section
'programing' 카테고리의 다른 글
UITableView에서 빈 셀을 삭제하는 방법 (0) | 2023.04.22 |
---|---|
Git serve : 그렇게 심플하게 하고 싶다 (0) | 2023.04.22 |
동일한 키를 가진 이 유형의 다른 인스턴스가 이미 추적 중이므로 엔티티 유형의 인스턴스를 추적할 수 없습니다. (0) | 2023.04.22 |
Git 별칭 나열 (0) | 2023.04.22 |
윈도우즈 10에서 환경 변수가 너무 큽니다. (0) | 2023.04.22 |