10.12(금) 실습-1( 정적/동적 TLS , 공유메모리 )

2007/10/14 19:41

1. 정적 TLS
[ more.. | less.. ]
// 정적 TLS의 문제점 : DLL에 만들고.. 해당 DLL을 LoadLibrary로 사용하면 TLS에 있는 변수가
//                   제대로 동작하지 않는다. => 동적 TLS를 사용하자.                   

// 3의 배수를 차례대로 리턴하는 함수
// single Thread에서는 문제없다.
int Next3Times()
{
    // 정적 TLS : static 지역 변수 혹은 전역 변수에만 사용가능..
    __declspec(thread) static int n = 0;
    n = n + 3;
    return n;
}

DWORD WINAPI foo( void* p )
{
    char* n = (char*)p;

    printf( "%s : %d\n", n, Next3Times() );
    printf( "%s : %d\n", n, Next3Times() );
    printf( "%s : %d\n", n, Next3Times() );

    return 0;
}

int main()
{
    HANDLE h[2];
    h[0] = CreateThread( 0, 0, foo, "A", 0, 0 );
    h[1] = CreateThread( 0, 0, foo, "\tB", 0, 0 );

    getch();
}

2. 동적 TLS
[ more.. | less.. ]
// 전역변수( 모든 스레드 공유 )로 TLS Index를 보관한다.
DWORD index = 0;

int Next3Times()
{
    DWORD n = (DWORD)TlsGetValue( index ); // TLS에서 값을 꺼내서
    n = n + 3;  // 증가하고
    TlsSetValue( index, (LPVOID) n );    // 다시 보관한다.

    return n;
}

DWORD WINAPI foo( void* p )
{
    // TLS 초기값을 설정한다.
    TlsSetValue( index, 0 );

    char* n = (char*)p;

    printf( "%s : %d\n", n, Next3Times() );
    printf( "%s : %d\n", n, Next3Times() );
    printf( "%s : %d\n", n, Next3Times() );

    return 0;
}

int main()
{
    // 스레드를 생성하기 전에 동적으로 TLS의 빈 슬롯(4byte)을 할당한다.
    index = TlsAlloc();

    HANDLE h[2];
    h[0] = CreateThread( 0, 0, foo, "A", 0, 0 );
    h[1] = CreateThread( 0, 0, foo, "\tB", 0, 0 );

    WaitForMultipleObjects( 2, h, TRUE, INFINITE );

    TlsFree( index );  // TLS를 해지한다.
}

3. 알고리즘 시간을 측정
[ more.. | less.. ]
void get_thread_time( HANDLE hThread, __int64* usertime, __int64* kerneltime )
{
    // 스레드의 생성시간, 종료시간, 커널시간, user시간을 구한다.
    __int64 t1, t2;

    GetThreadTimes( hThread, (FILETIME*)&t1,    // 생성시간
                             (FILETIME*)&t2,    // 종료시간
                             (FILETIME*)kerneltime,    // 커널에서 실행한 시간
                             (FILETIME*)usertime );     // 유저모드에서 실행한 시간

}
int factorial( int a )
{
    if( a == 1 ) return 1;

    return a * factorial( a-1 );
}

int main()
{
    __int64 k1 = 0, k2 = 0, u1 = 0, u2 = 0;

    get_thread_time( GetCurrentThread(), &u1, &k1 );

    // 시간을 측정하고 싶은 알고리즘
    for ( int i = 0; i < 1000; ++i )
        printf( "%d\n", factorial(10) );

    get_thread_time( GetCurrentThread(), &u2, &k2 );

    printf( "걸린시간 : %d ms\n", ( (u2-u1) + (k2-k1))/10);
}

4. 스레드에 메시지 루프 집어넣기
[ more.. | less.. ]
#include <commctrl.h> // 공용 컨트롤 헤더
// 스레드간 메세지 통신...
DWORD WINAPI foo( void* p )
{
    TCHAR szApp[] = _T("BBB");

    ATOM atom;
    WNDCLASS wc;
    HWND hwnd;
    MSG msg;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hbrBackground= (HBRUSH)GetStockObject( WHITE_BRUSH );
    wc.hCursor        = LoadCursor( 0, IDC_ARROW );
    wc.hIcon        = LoadIcon( 0, IDI_APPLICATION);
    wc.hInstance    = GetModuleHandle(0);
    wc.lpfnWndProc  = WndProc;
    wc.lpszClassName= szApp;
    wc.lpszMenuName = 0;
    wc.style        = 0;
    atom = RegisterClass( &wc);
   
    if ( atom == 0 )
    {
        MessageBox( 0, _T("Fail To RegisterClass"), _T("Error"), MB_OK);
        return 0;
    }
    hwnd = CreateWindowEx( 0, szApp, szApp, WS_OVERLAPPEDWINDOW,
                                CW_USEDEFAULT, 0, CW_USEDEFAULT,0, 0, 0,
                                0, 0);
    ShowWindow( hwnd, SW_SHOW);
    UpdateWindow( hwnd );

    while ( GetMessage( &msg, 0, 0, 0) )
    {                           
        TranslateMessage(&msg);
        DispatchMessage( &msg);
    }

    return 0;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HWND hPrg;

    switch( msg )
    {   
    case WM_CREATE:
       
        hPrg = CreateWindow( PROGRESS_CLASS, "", WS_CHILD | WS_VISIBLE | WS_BORDER,
                                10,10,500,30, hwnd, (HMENU)1, 0,0);
        SendMessage( hPrg, PBM_SETRANGE32,0,1000);
        return 0;
    case WM_LBUTTONDOWN:
        {
            HANDLE h = CreateThread( 0, 0, foo, (void*)hwnd, 0, 0);

        }
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam);
}

5. 스레드에서 주 스레드로 메시지 보내기
[ more.. | less.. ]
// 스레드간 메세지 통신...
DWORD WINAPI foo( void* p )
{
    HWND hwnd = (HWND)p;

    for ( int i=0; i< 1000; ++i )
    {
       // 생성된 프로그레스바에 메시지를 보낸다.
       SendMessage( hPrg, PBM_SETPOS, i, 0);
       Sleep(10);


        // 주스레드에 직접 메세지를 보내서 자신을 스스로 Update가 되게 하다.
        BOOL b = PostMessage( hwnd, WM_USER+100, i, 0);
        if ( b == FALSE ) break;
        Sleep( 10 );
    }
    return 0;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HWND hPrg;

    switch( msg )
    {   
    case WM_CREATE:
       
        hPrg = CreateWindow( PROGRESS_CLASS, "", WS_CHILD | WS_VISIBLE | WS_BORDER,
                                10,10,500,30, hwnd, (HMENU)1, 0,0);
        SendMessage( hPrg, PBM_SETRANGE32,0,1000);
        return 0;

        case WM_RBUTTONDOWN:
            {
                while(1);
            }
            return 0;

    case WM_LBUTTONDOWN:
        {
            HANDLE h = CreateThread( 0, 0, foo, (void*)hwnd, 0, 0);

        }
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam);
}

6. 공유메모리
[ more.. | less.. ]
// 일반 전역 변수..  .data section에 놓인다. 기본적으로 COW하는 속성을 가지고 있다.
int x = 0;

// exe에 새로운 data 섹션을 만든다.
#pragma data_seg("AAA")
int y = 0;      // 공유 섹션으로 만드려면 반드시 초기값이 필요하다.
#pragma data_seg()

// 특정 섹션의 속성을 지정한다. - Read, Write, Share( COW 속성이 제거된다. )
#pragma comment( linker, "/section:AAA,RWS")    // Copy On Write

int main()
{
    ++x;    ++y;

    if( y >= 3 )
    {
        printf( "3번 이상은 실행하지 마세요\n" );
        --y;
        return 0;
    }

    printf( "x = %d, y = %d\n", x, y );

    _getch(); 
}

7. 공유메모리의 DLL화
[ more.. | less.. ]
// SHARED.dll만들기
// 공유 메모리는 반드시 초기화 해야 한다.,!!!!!!!!!!!!!!

#pragma data_seg("SHARED")
char buf[1024] = { 0 };
#pragma data_seg()

#pragma comment( linker, "/section:SHARED,RWS" )
/////////////////////////////////////////////////////

extern "C" __declspec(dllexport) void SetData(char* s)
{
    strcpy( buf, s );
}
extern "C" __declspec(dllexport) void GetData(char* s)
{
    strcpy( s, buf );
}
/////////////////////////////////////////////////////////////////////////////
// A.cpp
typedef void(*F)(char*);
int main()
{
   
HMODULE hDll = LoadLibrary( "SHARED.dll" );
    F SetData = (F)GetProcAddress( hDll,
"SetData" );
    F GetData = (F)GetProcAddress( hDll,
"GetData" );

   
HANDLE hEvent = CreateEvent( 0, 0, 0, "e" );
   
HANDLE hEvent2 = CreateEvent( 0, 0, TRUE, "e2" ); // 최초의 signal은 통과해라.

    while( 1 )
    {
       
char buf[256] = { 0 };

        WaitForSingleObject( hEvent,
INFINITE );

       
// DLL의 공유 버퍼에서 Data를 꺼낸다.
        GetData( buf );

        SetEvent( hEvent2 );

        printf(
"읽어온 data : %s\n", buf );
    }
}
///////////////////////////////////////////////////////////////////////////
// B.cpp
typedef void(*F)(char*);

int main()
{
    HMODULE hDll = LoadLibrary( "SHARED.dll" );
    F SetData = (F)GetProcAddress( hDll, "SetData" );
    F GetData = (F)GetProcAddress( hDll, "GetData" );

    HANDLE hEvent = CreateEvent( 0, 0, 0, "e" );
    HANDLE hEvent2 = CreateEvent( 0, 0, TRUE, "e2" ); // 최초의 signal은 통과해라.
   
    while( 1 )
    {
        char buf[256] = { 0 };
        gets( buf );    // 1줄입력

        // 정말 가져 갔는지 확인한다.
        WaitForSingleObject( hEvent2, INFINITE );

        SetData( buf );

        // 대기중인 스레드를 깨운다.
        SetEvent( hEvent );
    }
}


Tags

System, 실습