리눅스, Windows 관점에서 설명해주세요.
나는 C #으로 프로그래밍하고 있는데,이 두 용어가 차이를 만들 것입니다. 예를 들어 가능한 한 많이 게시하십시오 ....
감사
리눅스, Windows 관점에서 설명해주세요.
나는 C #으로 프로그래밍하고 있는데,이 두 용어가 차이를 만들 것입니다. 예를 들어 가능한 한 많이 게시하십시오 ....
감사
답변:
Windows의 경우 중요 섹션은 뮤텍스보다 가볍습니다.
뮤텍스는 프로세스간에 공유 될 수 있지만 항상 약간의 오버 헤드가있는 커널에 대한 시스템 호출이 발생합니다.
중요 섹션은 하나의 프로세스 내에서만 사용할 수 있지만 경합의 경우 커널 모드로만 전환 할 수 있다는 장점이 있습니다. 일반적이지 않은 비경쟁 획득은 매우 빠릅니다. 경합의 경우, 이벤트 또는 세마포어와 같은 일부 동기화 기본 요소를 대기하기 위해 커널로 들어갑니다.
나는 둘 사이의 시간을 비교하는 빠른 샘플 앱을 작성했습니다. 1,000,000 개의 비경쟁 획득 및 릴리스 시스템에서 뮤텍스가 1 초 이상 걸립니다. 1,000,000 획득에 중요한 섹션은 ~ 50 ms가 소요됩니다.
테스트 코드는 다음과 같습니다. mutex가 첫 번째 또는 두 번째 인 경우 비슷한 결과를 얻었으므로 다른 효과는 보이지 않습니다.
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);
이론적 인 관점에서 중요한 섹션 은 코드가 공유 리소스에 액세스하기 때문에 여러 스레드에서 한 번에 실행하면 안되는 코드입니다.
뮤텍스 임계 영역을 보호하기 위해 사용된다 (때로는 데이터 구조의 이름) 알고리즘이다.
실제로 Windows에서 사용 가능한 많은 뮤텍스 구현이 있습니다. 잠금 수준, 범위, 비용 및 다양한 수준의 경합에서 성능에 따라 구현 결과에 따라 다릅니다. 다양한 뮤텍스 구현 비용에 대한 차트는 확장 성 을 위해 CLR 인사이드 아웃-동시성 사용을 참조하십시오 .
사용 가능한 동기화 기본 요소.
lock(object)
문은 사용하여 구현됩니다 Monitor
- 참조 MSDN을 참조.
지난 몇 년 동안 비 차단 동기화 에 대한 많은 연구가 이루어졌습니다 . 목표는 잠금 또는 대기없는 방식으로 알고리즘을 구현하는 것입니다. 이러한 알고리즘에서 프로세스는 다른 프로세스가 작업을 완료하도록 도와 프로세스가 최종적으로 작업을 완료 할 수 있도록합니다. 결과적으로 일부 작업을 수행하려는 다른 프로세스가 중단 된 경우에도 프로세스가 작업을 완료 할 수 있습니다. Usinig 잠금 장치는 잠금을 해제하지 않고 다른 프로세스가 계속 진행되지 않도록합니다.
다른 답변 외에도 다음 세부 정보는 창의 중요 섹션에만 적용됩니다.
InterlockedCompareExchange
작업 만큼 간단 합니다.리눅스에서 나는 그들이 스핀 카운트와 중요한 섹션과 유사한 목적을 제공하는 "스핀 잠금"을 가지고 있다고 생각합니다.
중요 섹션 및 Mutex는 운영 체제별로 다르며 멀티 스레딩 / 멀티 프로세싱의 개념입니다.
중요 섹션 주어진 시간에 자체적으로 만 실행해야하는 코드입니다 (예 : 5 개의 스레드가 동시에 실행되고 "critical_section_function"이라는 함수가 배열을 업데이트합니다 ... 5 개의 스레드를 모두 원하지는 않습니다. 따라서 프로그램이 critical_section_function ()을 실행할 때 다른 스레드는 critical_section_function을 실행해서는 안됩니다.
mutex * Mutex는 임계 섹션 코드를 구현하는 방법입니다 (토큰처럼 생각하십시오. 스레드는 critical_section_code를 실행하기 위해이를 보유해야합니다).
뮤텍스는 스레드가 획득 할 수있는 객체로, 다른 스레드가이를 획득하지 못하게합니다. 필수 사항이 아닌 권고입니다. 스레드는 뮤텍스가 나타내는 자원을 획득하지 않고 사용할 수 있습니다.
중요 섹션은 운영 체제에서 작동하지 않도록 보장되는 코드 길이입니다. 의사 코드에서는 다음과 같습니다.
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
Linux에서 중요 선택과 동일한 '빠른'Windows는 futex 이며 이는 빠른 사용자 공간 뮤텍스를 나타냅니다. futex와 mutex의 차이점은 futex의 경우 중재가 필요할 때만 커널이 관여하므로 원자 카운터가 수정 될 때마다 커널과 통신하는 오버 헤드가 절약된다는 것입니다. .. 일부 응용 프로그램에서 잠금을 협상 하는 데 상당한 시간을 절약 할 수 있습니다 .
뮤텍스를 공유하기 위해 사용하는 수단을 사용하여 프로세스간에 퓨 텍스를 공유 할 수도 있습니다.
불행하게도, futex는 구현하기 가 매우 까다로울 수 있습니다 (PDF). (2018 업데이트, 그들은 2009 년만큼 거의 무섭지 않습니다).
그 외에도 두 플랫폼에서 거의 동일합니다. 기아를 유발하지 않는 방식으로 공유 구조에 대한 원 자성 토큰 기반 업데이트를 만들고 있습니다. 남아있는 것은 단순히 그것을 달성하는 방법입니다.
내 2 센트를 추가하기 위해 중요한 섹션은 구조로 정의되고 해당 섹션에 대한 작업은 사용자 모드 컨텍스트에서 수행됩니다.
ntdll! _RTL_CRITICAL_SECTION + 0x000 DebugInfo : Ptr32 _RTL_CRITICAL_SECTION_DEBUG + 0x004 LockCount : Int4B + 0x008 재귀 카운트 : Int4B + 0x00c 소유 스레드 : Ptr32 Void + 0x010 LockSemaphore : Ptr32 무효 + 0x014 SpinCount : Uint4B
뮤텍스는 Windows 객체 디렉토리에 생성 된 커널 객체 (ExMutantObjectType)입니다. 뮤텍스 작업은 대부분 커널 모드에서 구현됩니다. 예를 들어, Mutex를 만들 때 커널에서 nt! NtCreateMutant를 호출하게됩니다.
마이클의 훌륭한 답변. C ++ 11에 도입 된 뮤텍스 클래스에 대한 세 번째 테스트를 추가했습니다. 결과는 다소 흥미롭고 단일 프로세스에 대한 CRITICAL_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의 시간 비율과 거의 비슷하지만 내 컴퓨터는 그의 컴퓨터보다 최소 4 년 어리기 때문에 2009 년과 2013 년 사이에 속도가 증가했다는 증거를 볼 수 있습니다. XPS-8700이 나왔을 때). 새로운 뮤텍스 클래스는 Windows 뮤텍스보다 2 배 빠르지 만 여전히 Windows CRITICAL_SECTION 객체 속도의 10 분의 1보다 느립니다. 비 재귀 뮤텍스 만 테스트했습니다. CRITICAL_SECTION 객체는 재귀 적입니다 (한 번의 스레드가 동일한 횟수를 남겨두면 반복적으로 입력 할 수 있습니다).
실제 기능 만 사용하는 경우 AC 기능을 재진입이라고합니다.
재진입 함수는 동시에 여러 스레드에서 호출 할 수 있습니다.
재진입 기능의 예 :
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;
}
C 표준 라이브러리 strtok ()은 재진입 할 수 없으며 동시에 두 개 이상의 스레드에서 사용할 수 없습니다.
일부 플랫폼 SDK에는 strtok_r ()이라는 재진입 버전의 strtok ()이 제공됩니다.
엔리코 밀리오레