Atomic Operation
#include <stdio.h>
#include <Windows.h>
#include <process.h>
LONG x = 0; // 모든 스레드 공유
UINT __stdcall foo(void* p)
{
for(int i = 0 i < 1000000; i++)
{
x = x + 1;
}
return 0;
}
int main()
{
HANDLE h1 = (HANDLE)_beginthreadex(0, 0, foo, 0, 0, 0);
HANDLE h2 = (HANDLE)_beginthreadex(0, 0, foo, 0, 0, 0);
HANDLE h3 = (HANDLE)_beginthreadex(0, 0, foo, 0, 0, 0);
HANDLE h[3] = {h1, h2, h3};
WaitForMultipleObjects(3, h, TRUE, INFINITE);
printf("result : %d\n", x);
return 0;
}
UINT __stdcall foo(void* p)
{
for(int i = 0 i < 1000000; i++)
{
// x = x + 1;
__asm
{
mov eax, x // 1번 thread가 eax로 x값을 가져간 상태에서
add eax, 1
mov x, eax // 2번 thread가 x에 eax에 1을 더한값을 넣는다면? -> 이러한이유로 원하는 값이 안나옴
}
}
return 0;
}
해결책?
UINT __stdcall foo(void* p)
{
for(int i = 0 i < 1000000; i++)
{
// x = x + 1;
__asm
{
inc x // 이 어셈블리 명령은 어셈블리 명령중 다른 thread가 사용하지 못하게 함.
// 단, muti CPU를 사용할 경우 물리적으로 CPU가 다르기에 역시 정상적으로 동작하지 않음.
}
}
return 0;
}
UINT __stdcall foo(void* p)
{
for(int i = 0 i < 1000000; i++)
{
// x = x + 1;
__asm
{
lock inc x // 아에 lock을 걸어야한다.
}
}
return 0;
}
무조건 어셈블리를 써야하나? -> Nope
UINT __stdcall foo(void* p)
{
for(int i = 0 i < 1000000; i++)
{
// x = x + 1;
InterlockedIncreament(&x); // 내부적으로 lock inc를 사용
// (참고) 브레이크 포인트를 여기 걸고 Alt+F8을 누르면, 기게어코드를 보여줌.
}
return 0;
}
- Atomic operation
InterlockedIncreament
InterlockedXXX
Thread Local Storage
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
#include <process.h>
// 호출할때 마다 3의 배수를 차례대로 반환
int next3times()
{
static int n = 0;
n = n + 3;
return n;
}
UINT __stdcall foo(void* p)
{
printf("%s : %d\n", next3times()); // 3
printf("%s : %d\n", next3times()); // 6
printf("%s : %d\n", next3times()); // 9
return 0;
}
int main()
{
HANDLE h1 = (HANDLE)_beginthreadex(0, 0, foo, (void*)"A", 0, 0);
HANDLE h2 = (HANDLE)_beginthreadex(0, 0, foo, (void*)"\tB", 0, 0);
getchar();
return 0;
}
결과는?
B : 3
B : 6
B : 9
A : 3
A : 12
A : 15
매번 이렇진 않지만 어쨋든 예상했던 결과와는 다르게 나온다.
Thread별로 next3times()
가 동작하게 해보자(Thread당 data(static) 메모리를 갖게하자) -> Thread Local Storage
int next3times()
{
// TLS(Thread Local Storage)에 넣어달라
__declspec(thread) static int n = 0;
n = n + 3;
return n;
}
B : 3
B : 6
B : 9
A : 3
A : 6
A : 9
__declspec(thread)
는 지역변수에 붙일 수 없다. static전역, 전역변수에만 붙일 수 있음.