글 목록 보기

Lyn
조회 수 37446 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

지인이 물어온 김에 정리 해 둡니다....



Windows Thread API 는 고성능이지만 치명적인 단점이 하나 있(었)는데... 바로 대기하는 Thread를 몽땅 한번에 다 깨우는 방법이 없다는 겁니다.
Event 객체는 유용하지만 하나의 쓰래드만 깨우거나(오토리셋 이벤트) 아니면 락이 다 풀리거나(메뉴얼리셋 이벤트) 둘중 하나의 동작만을 하게됩니다.


Java에 익숙하신 분이라면 notifyAll을 유용하게 사용하셨을 거고 pthread 라이브러리를 쓰시는 분도 역시 자주 사용 하실 겁니다.


그래서 거기에 대한 몇가지 꼼수를 프로그래밍 하다 보면 만나볼 수 있는데


1. 무한 루프 돌면서 상태를 체크한다 

- CPU 낭비도 이만한게 없습니다.

2. 메뉴얼 리셋 이벤트를 이용해서 잠깐 락을 풀고 다시 건다 

- 락이 걸리지 않고 그냥 지나칠 가능성이 있어 위험합니다.

3. pthread나 ACE에 포팅된 상태변수를 가져다 쓴다 해결책이 아니잖아


실제로 ACE는 상태변수를 자체 구현하고 있는데 그 대략적인 방법은 아래와 같습니다.

1. 오토 리셋 이벤트와 크리티컬 섹션을 생성한다

2. join 할 때 마다 카운트를 증가시킨다

3. 이벤트 발생시 증가된 카운트만큼 이벤트를 Set 한다.



뭐 그리 복잡한 방법도 아니고 구현 자체는 어렵지 않은데...
어쨋든 OS의 부족한 기능을 외부에서 가져오자니 좀 그렇습니다.


어쨋든 Windows Vista/2008 부터는 여기에 대응하는 API가 추가되었습니다.
WakeAllConditionVariable라는 놈인데요. 말 그대로 모든놈 다 깨우라는 뜻입니다.


pthread의 상태변수가 그런것 처럼 상태변수 자체만으론 Lock 의 기능을 하지 않고 다른 락의 힘을 빌리게 되는데 기본적으론 Critical Section 을 사용하게 됩니다.
이 다음은 귀찮으니 소스 보면서 하겠습니다.


#include <Windows.h>
#include <cstdio>

CRITICAL_SECTION cs;
CONDITION_VARIABLE cv;

DWORD PASCAL TestThread(LPVOID pParam)
{
	EnterCriticalSection(&cs);
	
	SleepConditionVariableCS(&cv, &cs, INFINITE);
	printf("%d\n", GetCurrentThreadId());
	LeaveCriticalSection(&cs);

	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	InitializeCriticalSection(&cs);
	InitializeConditionVariable(&cv);

	HANDLE Threads[10];
	for(int i = 0; i < 10; ++i)
	{
		Threads[i] = CreateThread(nullptr, 0, &TestThread, nullptr, 0, nullptr);
	}
	Sleep(1000);
	WakeAllConditionVariable(&cv);
	WaitForMultipleObjects(10, Threads, TRUE, INFINITE);
	DeleteCriticalSection(&cs);
	return 0;
}


CriticalSection 에 추가적으로 ConditionVariable 을 초기화 시켜 준 후, WaitForSingleObject 대신 SleepConditionVariableCS을 사용합니다(만약 Read/Write Lock 을 사용중이라면 SleepConditionVariableSRW을 대신 이용합니다)


그리고 Thread가 대기하고 있을때 WakeAllConditionVariable를 이용해서 쓰래드를 몽땅 깨워 주면 됩니다.
당연히 대기중인 Thread가 하나도 없을땐 아무 일도 하지 않습니다.


만약 하나만 깨운다면 WakeConditionVariable를 사용할 수 있습니다.
하나만 깨운다는면에서는 동일하지만 Event 와는 동작이 미묘하게 다른것이 이벤트는 대기중인 Thread가 없어도 Set 상태가 되면 언제라도 통과할 수 있지만 WakeConditionVariable은 대기중인 Thread가 없으면 아무 일도 하지 않습니다.


덤으로 Condition Variable 은 해제 함수가 없습니다. 그냥 냅두시면 됩니다.


TAG •
?

Atachment
첨부 '3'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

C++ 템플릿 상속엔 골 때리는 문제가 있는데 ...

바로 베이스클래스의 멤버에 접근하려면 베이스 클래스의 이름을 명시해야 한다는 것이다... 안그러면 이름을 못찾아온다

사람 상당히 귀찮게 만드는놈이라 할 수 있겠다.

보통 일반적으론 볼일이 없고 Mixin 을 이용하거나 아니면 Effective C++ 을 읽으면서 알게된다(책에는 "못찾는다" 라고 되어 있다)


간단히 말하면 이런 코드다

#include <cstdio>

void foo()
{
	printf("Global Function\n");
}
template <typename T>
class Cls1
{
	T Var1;
public:
	void foo()
	{
		printf("Member Function\n");
	}
};

template <typename T>
class Cls2 : public Cls1<T>
{
	T Var2;
public:
	void test()
	{
		Cls1<T>::foo();
	}
};

int main()
{
	Cls2<int> cls2;
	cls2.test();

	return 0;
}


근데 만약 명시 하지 않으면 어떻게 될까?


일단 최신의 VC++ 2012에서 돌려보자

01.png


헐...? 정확히 멤버 함수를 찾아온다. 여기엔 올리지 않겠지만 VS2005~2010 도 모두 같은 동작을 한다.

다른컴파일러에서 해보자. gcc 4.4다.


02.png


헐?! 글로벌 함수를 찾는다 (...) 이거뭥미.


그럼 최신의 C++0x를 지원하는 gcc4.7에서 해보자.


03.png


역시 전역함수를 찾는다.


표준문서는 귀찮아서 (...) 확인하지 않았지만 관련 서적 등을 보면 gcc의 동작이 맞는 것으로 보인다.
하지만 저런 코드를 짜면서 gcc 쪽 동작을 원하는 사람이 과연 있을까... 하는 생각이 든다. 일반적인 클래스를 만드는 느낌으로 당연히 멤버함수가 호출 될 것이라고 생각 하지 않을까? 이 결과를 확인하면서 C++은 당최 생각대로 움직여 주지 않는 매저키스트 들을 위한 언어란 누군가의 말이 귓가에 맴돈다 (...)


결론 : EC++에 나온대로 그냥 제대로 포커스 지정 하자 (...) 야!


PS. gcc 에서는 저렇게 두가지 모두 있는 경우가 아니라 전역 foo 함수가 없는 (즉 이름을 찾을 수 없는) 경우에는 당연히 이름을 찾지 못해 에러를 내는데, 이 경우 컴파일 옵션에 -fpermissive 를 추가 하는 것으로 멤버함수로의 연결이 가능하다(워닝은 뜬다)
단 전역 foo 함수가 존재 할 경우 저 옵션을 그냥 무시하고 그냥 전역 함수로 연결해 버린다.


PS2. 혹시 LLVM-clang 을 사용할 수 있는 환경에 계신분은 어떤 결과가 나오는지 알려주시면 좋겠다.



?
  • ?
    123 2012.09.02 19:18
    VS 에서는 자동으로 찾아주네요.. 역시 상용이라 틀린가보네요..
    clang 으로 컴파일 해서 실행해보니 gcc 하고 같네요. .
    관련글에는 이렇게 나오네요
    While non-dependent names are resolved "normally" – when the template is defined,
    the resolution for dependent names happens at the point of the template’s instantiation.
    그러까 템플릿 인스턴스시 resolve 가 되게할려면 dependent name 으로 만들어 줘야 되네요
    Cls1<T>::foo() or this->foo() 이런식으로..

    http://eli.thegreenplace.net/2012/02/06/dependent-name-lookup-for-c-templates/

2012.08.17 02:06

[윈도8] 윈도8 구경1

Lyn
조회 수 26201 추천 수 0 댓글 1
Atachment
첨부 '2'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

얼마전 MSDN 에 정식으로 올라온 윈도8 입니다.
Enterprise 버전은 라이센스가 없어서 Pro로 설치 했네요


Untitled.png


슬슬 제대로 둘러봐야 겠지만 첫인상 아주 좋습니다.
빠릿빠릿해요 그냥


아직 호환성이 걱정되는 놈들이 있어 Win7하고 듀얼부팅을 세팅 해 뒀는데... OS 선택 메뉴가 아주 화려하네요.


dual boot.png

?
  • ?
    사무엘 2012.09.11 18:52
    윈8.. 윈도우 XP 이래로 트렌드이던 둥근 모서리가 없어지고,
    제목이 창 제목의 왼쪽이 아니라 가운데에 찍히는 디자인이 윈도우 3.x 이래로 다시 부활했고,
    그러데이션이 그냥 단색(solid color)로 바뀌는 등..
    모바일을 의식해서 그런지 디자인이 좀 더 검소해지고 단순해졌지요. 단순히 변화만을 위한 변화인 것도 느껴지더군요.

    일부 너무 급격한 변화는 왜 이런 걸 만들었는지 의문이 드는 것도 있습니다.
    참, MS 오피스는 2007 이래로 진작부터 제목이 가운데에 찍히는 스타일로 회귀했지요. ^^

Lyn
조회 수 25569 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
void foo1(string str = "DefaultString")
{
//값으로넘기기
}
 
void foo2(const string str = "DefaultString")
{
//const 값으로넘기기
}
 
void foo3(string& str = "DefaultString")
{
//이건 에러남...
}
 
void foo4(const string& str = "DefaultString")
{
//const 레퍼런스로 넘기기
}
 
void foo5(string& name = string("DefaultString"))
{
//임시객체 넘기기
}
 
void foo6(const string& name = string("DefaultString"))
{
//const 임시객체 넘기기
}
 
static std::string DEFAULT_STRING = "DefaultString";
void foo7(string& str = DEFAULT_STRING)
{
//이건 DEFAULT_STRING이 바뀔 가능성이 있음.. 위험한코드
}
 
const static std::string CONST_DEFAULT_STRING = "DefaultString";
void foo8(const string& str = CONST_DEFAULT_STRING)
{
//static const 레퍼런스 객체 넘기기 (boost가 자주 쓴 방식... string은 아니지만)
}
?

Lyn
조회 수 27346 추천 수 0 댓글 2
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

Devpia에 자꾸 질문이 올라와서 정리 해 둔다 ...


1. 32bit 프로그램에서는 64bit DLL을 사용할 수 없다. 반대도 마찬가지


2. 32bit 프로그램에서는 64bit ActiveX를 사용할 수 없다. 역시 반대도 마찬가지.


3. 64bit OS에서 32bit 프로그램이 Windows\System32에 접근하면 Windows\SysWOW64로 리다이렉트 된다. 


4. 32bit 프로그램에서는 64bit 프로세스의 정보를 얻어올 수 없다.


5. 64bit OS에서는 32bit 로 된 ShellExtension 을 띄울 수 없다. 단 32bit 프로그램이 ShellAPI로 ShellExtension 을 띄우려면 32bit용 ShellExtinsion 이 설치되어 있어야 한다(간단히 말해서 32/64 둘다만들란 소리다!)


6. ODBC는 32bit 와 64bit를 따로 등록해야 한다(64bit 의 제어판에 있는건 64bit 용 ODBC다. 32bit용은 SysWOW64에 가서 실행해야 한다)

한쪽만 등록할 경우 bit가 다르면 안보인다.


7. 윈도우 서비스는 공통이다. 신경 안써도 됨

?
  • ?
    ssonacy 2012.07.20 16:31

    64bit  프로세스는 32bit DLL 를 로드 할수 없다구요 ?

    흠 ...??

  • profile
    Lyn 2012.07.21 13:00

    네 없습니당


Lyn
조회 수 26674 추천 수 0 댓글 0
Atachment
첨부 '1'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
#include <windows.h>
#include <cstdio>
#include <cstdlib>

bool isThreadWaiting(HANDLE ThreadHandle)
{
	bool Result = false;

	SuspendThread(ThreadHandle);
	CONTEXT ThreadContext;
	ThreadContext.ContextFlags = CONTEXT_CONTROL;
	BOOL a = GetThreadContext(ThreadHandle, &ThreadContext);
	INT_PTR* StackPointer = reinterpret_cast<INT_PTR*>(ThreadContext.Esp);

	HMODULE DLLHandle = LoadLibraryA("Ntdll.dll");
	INT_PTR WaitFunctionPtr = reinterpret_cast<INT_PTR>(GetProcAddress(DLLHandle, "NtWaitForSingleObject")); 
	//WaitForSingleObject의 종착점이 되는 함수가 NtWaitForSingleObject
	
	if ((*StackPointer >= WaitFunctionPtr) && (*StackPointer <= WaitFunctionPtr + 50))
	{
		//대충 +50을 해 주는 이유는 실제 복귀주소로 저장된 곳이 NtWaitForSingleObject의 시작주소가 아니라 조금 실행된 뒤이기 때문
		Result = true;		
	}

	FreeLibrary(DLLHandle);
	ResumeThread(ThreadHandle);

	return Result;
}

DWORD PASCAL ThreadFun1(PVOID Param)
{
	WaitForSingleObject(HANDLE(Param), INFINITE);

	return 0;
}

DWORD PASCAL ThreadFun2(PVOID Param)
{
	while(true)
	{
		Sleep(100000);
	}

	return 0;
}
void main()
{	
	HANDLE Event = CreateEvent(nullptr, TRUE, FALSE, nullptr);

	HANDLE Thread1 = CreateThread(nullptr, 0, &ThreadFun1, reinterpret_cast<LPDWORD>(Event), 0, nullptr);
	HANDLE Thread2 = CreateThread(nullptr, 0, &ThreadFun2, nullptr, 0, nullptr);

	Sleep(1000); //Thread 돌 시간좀 주고 ...

	printf("Is Thread1 Waiting? %s\n", isThreadWaiting(Thread1) ? "Yes" : "No");
	printf("Is Thread2 Waiting? %s\n", isThreadWaiting(Thread2) ? "Yes" : "No");

	system("pause");
}


sc1.png


Wait계열 함수가 몇종류 더 있는데 그것도 그냥 각자 처리해 주면 간단...


?

Lyn
조회 수 24220 추천 수 0 댓글 0
Atachment
첨부 '1'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
<작성중>
소스는 첨부파일에. 
#include <cstdio>
#include <windows.h>
#include "Coroutine.h"
 
class NumberCount : public Coroutine<int>
{
protected:
	void Execute() override
	{
		for (int i = 0; i < 100; ++i)
		{			
			yield_return(i);
		}
	}
};
 
class NumberPrint : public Coroutine<void>
{
protected:
	void Execute() override
	{
		for (int i = 0; i < 100; ++i)
		{		
			printf("NumberPrint : %d\n", i);
			yield;
		}
	}
};
 
void wmain()
{	
	ConvertThreadToFiber((LPVOID)GetCurrentThreadId());
	
	NumberCount Co1;
	NumberPrint Co2;
 
	for (int i = 0; i < 100; ++i)
	{
		printf("NumberCount : %d\n", Co1.Resume());
		Co2.Resume();
		Sleep(1000);
	}	
}
 

?

Lyn
조회 수 24728 추천 수 0 댓글 0
Atachment
첨부 '1'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

1. v8 빌드


2. 사용법



?

2012.02.19 23:15

[작성중] 알려진 포트

Lyn
조회 수 24253 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

TCP


21 : Ftp

22 : SSH

80 : Http

443 : Https

3389 : RDP (Windows 원격 데스크탑)


?

Lyn
조회 수 30874 추천 수 0 댓글 4
Atachment
첨부 '1'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

C++의 STL 에서는 여러가지 알고리즘을 제공한다.
그중 "임의의" 동작을 할 수 있는 알고리즘들은 그 "동작" 의 대상이 되는 인자를 받도록 되어 있는데, 
그 "동작"의 조건은 ()연산자로 호출이 가능할것! 이다.


그 동작을 만족시키는것은 C에서는 함수가 있다.
또 C++에서는 STL 과 함께 소개된 함수객체(Functor)가 있으며, STL 초기부터 제공된 bind1st, bind2nd가 있고, boost에서 제공되는 mem_fn 도 있으며, 이 모든것을 통합한 범용 bind(C++11 or boost에 포함)도 존재한다.

그리고 최신의 C++11 표준에 포함된 lambda도 있고, boost의 lambda도 있지만, 이쪽은 C++11 표준에 lambda가 포함 된 이상 더이상 쓸 일은 없을테니 이번 실험에선 제외한다.


실험순서는 전역함수, bind된 전역함수, 함수객체, 람다, bind된 람다, auto를 이용해 bind된 람다 의 순이다.

실험은 Visual C++ 2010 Sp1, x86 Debug Build에서 테스트 하엿다. Release Mode 도 특성은 동일하다.

궂이 Debug Build로 테스트 한 이유는 Release모드에서 자꾸 전역함수를 inline화 시키는 바람에 =_=;


코드 나간다.


#include <cstdlib>
#include <cstdio>
#include <vector>
#include <random>
#include <cstdint>
#include <algorithm>
#include <functional>
#include <windows.h>
 
using namespace std;
 
__int64 g_NumberSum;
 
void GetSumFunction(int Num)
{
	g_NumberSum = g_NumberSum + Num;
}
 
struct GetSumFunctor
{
	void operator()(int Num)
	{
		g_NumberSum = g_NumberSum + Num;
	}
};
 
void wmain()
{
	const int NUMBER_COUNT = 100000000;
 
	mt19937_64 RandomEngine;
	uniform_int <> RandomRange(0, INT32_MAX);
	RandomEngine.seed(GetTickCount());
 
	vector<int> RandumNumbers;
	RandumNumbers.reserve(NUMBER_COUNT);
	for (int i = 0; i < NUMBER_COUNT; ++i)
	{
		RandumNumbers.push_back(RandomRange(RandomEngine));
	}
 
	DWORD Tick;
//function
	g_NumberSum = 0;
	Tick = GetTickCount();
	for_each(RandumNumbers.begin(), RandumNumbers.end(), GetSumFunction);
	printf("Global Function :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//bind function
	g_NumberSum = 0;
	function<void(int)> FunctionObject = bind(&GetSumFunction, tr1::placeholders::_1);
	Tick = GetTickCount();
	for_each(RandumNumbers.begin(), RandumNumbers.end(), FunctionObject);
	printf("Bind Function   :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//functor
	g_NumberSum = 0;
	Tick = GetTickCount();	
	for_each(RandumNumbers.begin(), RandumNumbers.end(), GetSumFunctor());
	printf("Functor         :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//C++ 11 Lambda
	g_NumberSum = 0;
	Tick = GetTickCount();
	for_each(RandumNumbers.begin(), RandumNumbers.end(), 
		[](int Num)
		{
			g_NumberSum = g_NumberSum + Num;
		});
	printf("C++11 Lambda    :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//C++11 Bind Lambda
	g_NumberSum = 0;
	Tick = GetTickCount();
	function<void(int)> BindLambda = 
		[](int Num)
		{
			g_NumberSum = g_NumberSum + Num;
		};
	for_each(RandumNumbers.begin(), RandumNumbers.end(), BindLambda);
	printf("Bind Lambda     :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//C++11 Auto bind Lambda
	g_NumberSum = 0;
	Tick = GetTickCount();
	auto AutoBindLambda = 
		[](int Num)
		{
			g_NumberSum = g_NumberSum + Num;
		};
	for_each(RandumNumbers.begin(), RandumNumbers.end(), AutoBindLambda);
	printf("Auto Bind Lambda:: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick); 
} 



아래는 결과이다.

lambda.png


결과를 보면 lambda 가 가장 빠른 것을 볼 수 있다. 길게 나불대지 않고 성능 이슈와 원인을 아래 간략히 적어본다.
1. 전역함수의 경우 함수 Call 오버헤드에 의해 속도가 조금 감소하엿다.

2. lambda, functor 는 사실상 오차범위 내의 차이라고 보인다.

3. bind lambda 의 경우는 std::fuction 객체가 함수호출 정보를 RTTI에 의존하는 특징때문에 속도를 왕창 까먹게 된다.

4. bind 된 전역함수의 경우는 RTTI의 오버헤드 + 함수호출로 인해 가장 느려진다.

5. auto를 사용해 변수에 저장한 lambda 는 std::fuction객체에 저장 하는 것이 아니라 일종의 inline 함수처럼 구현해준다. 그래서 오버헤드가 전혀 없이 빠른 속도를 보여준다. 즉 같은 함수객체를 2번이상 이용 할 경우 코드를 Copy&Paste 하지 않아도 auto를 이용하면 성능과 중복방지 두마리 코드를 다 잡을 수 있다.

?
  • ?
    elrha 2012.08.16 09:57

    으아 이거 좋은정보네요


    테스트는 안해봤지만 auto 객체 마우스 오버해보면 std::function으로 나올듯 한데..


    결국 컴파일러 지능테스트인건가요 ㅋㅋㅋㅋ

  • profile
    Lyn 2012.08.16 22:53

    아뇨 함수포인터 취급해요 ㅎㅎ


    컴파일 타임에 완전한 호출 정보를 알고있는 상황에서 궂이 오버헤드가 있는 std::function으로 컴파일할 이유가 없죠

  • ?
    11ho 2014.03.24 20:52
    감사합니다. 속이 다 후련하네요 ㅠㅠ
  • ?
    해피 2015.07.17 10:15
    퍼가용

Board Pagination Prev 1 ... 4 5 6 7 8 9 10 11 12 13 Next
/ 13