TN058: MFC 모듈 상태 구현TN058: MFC Module State Implementation

참고

다음 기술 노트는 온라인 설명서에 먼저 포함되어 있었으므로 업데이트되지 않았습니다.The following technical note has not been updated since it was first included in the online documentation. 따라서 일부 절차 및 항목은 만료되거나 올바르지 않을 수 있습니다.As a result, some procedures and topics might be out of date or incorrect. 최신 정보를 보려면 온라인 설명서 색인에서 관심 있는 항목을 검색하는 것이 좋습니다.For the latest information, it is recommended that you search for the topic of interest in the online documentation index.

이 기술 노트는 MFC "모듈 상태" 구문의 구현에 대해 설명합니다.This technical note describes the implementation of MFC "module state" constructs. 모듈 상태 구현에 대한 이해는 DLL(또는 OLE 내 프로세스 서버)에서 MFC 공유 DLL을 사용하는 데 중요합니다.An understanding of the module state implementation is critical for using the MFC shared DLLs from a DLL (or OLE in-process server).

이 메모를 읽기 전에 새 문서, Windows 및 보기 만들기에서"MFC 모듈의 상태 데이터 관리"를 참조하십시오.Before reading this note, refer to "Managing the State Data of MFC Modules" in Creating New Documents, Windows, and Views. 이 문서에는 이 주제에 대한 중요한 사용 정보 및 개요 정보가 포함되어 있습니다.This article contains important usage information and overview information on this subject.

개요Overview

MFC 상태 정보에는 모듈 상태, 프로세스 상태 및 스레드 상태의 세 가지 종류가 있습니다.There are three kinds of MFC state information: Module State, Process State, and Thread State. 경우에 따라 이러한 상태 형식을 결합할 수 있습니다.Sometimes these state types can be combined. 예를 들어 MFC의 핸들 맵은 모듈 로컬 및 스레드 로컬입니다.For example, MFC's handle maps are both module local and thread local. 이렇게 하면 두 개의 서로 다른 모듈이 각 스레드에 서로 다른 맵을 가질 수 있습니다.This allows two different modules to have different maps in each of their threads.

프로세스 상태와 스레드 상태는 비슷합니다.Process State and Thread State are similar. 이러한 데이터 항목은 일반적으로 전역 변수였지만 적절한 Win32s 지원 또는 적절한 다중 스레딩 지원을 위해 지정된 프로세스 또는 스레드에 특정해야 하는 항목입니다.These data items are things that have traditionally been global variables, but have need to be specific to a given process or thread for proper Win32s support or for proper multithreading support. 지정된 데이터 항목이 맞는 범주는 해당 항목과 프로세스 및 스레드 경계와 관련하여 원하는 의미 체계에 따라 다릅니다.Which category a given data item fits in depends on that item and its desired semantics with regard to process and thread boundaries.

모듈 상태는 로컬 또는 스레드 로컬프로세스인 진정한 글로벌 상태 또는 상태를 포함할 수 있다는 점에서 고유합니다.Module State is unique in that it can contain either truly global state or state that is process local or thread local. 또한 신속하게 전환 할 수 있습니다.In addition, it can be switched quickly.

모듈 상태 전환Module State Switching

각 스레드에는 "현재" 또는 "활성" 모듈 상태에 대한 포인터가 포함되어 있습니다(당연히 포인터는 MFC의 스레드 로컬 상태의 일부임).Each thread contains a pointer to the "current" or "active" module state (not surprisingly, the pointer is part of MFC's thread local state). 이 포인터는 실행 스레드가 OLE 컨트롤 또는 DLL로 호출하는 응용 프로그램 또는 응용 프로그램으로 다시 호출하는 OLE 컨트롤과 같은 모듈 경계를 통과할 때 변경됩니다.This pointer is changed when the thread of execution passes a module boundary, such as an application calling into an OLE Control or DLL, or an OLE Control calling back into an application.

현재 모듈 상태가 호출하여 AfxSetModuleState전환됩니다.The current module state is switched by calling AfxSetModuleState. 대부분의 경우 API를 직접 처리하지 않습니다.For the most part, you will never deal directly with the API. MFC는 대부분의 경우 WinMain, OLE 엔트리 포인트 AfxWndProc등에서 당신을 위해 호출합니다.MFC, in many cases, will call it for you (at WinMain, OLE entry-points, AfxWndProc, etc.). 이 작업은 특수 및 현재 WndProc모듈 상태를 알고 있는 특수(또는)에 WinMain DllMain정적으로 연결하여 작성하는 모든 구성 요소에서 수행됩니다.This is done in any component you write by statically linking in a special WndProc, and a special WinMain (or DllMain) that knows which module state should be current. DLLMODUL을 보면 이 코드를 볼 수 있습니다. CPP 또는 아모둘. MFC\SRC 디렉토리의 CPP입니다.You can see this code by looking at DLLMODUL.CPP or APPMODUL.CPP in the MFC\SRC directory.

모듈 상태를 설정한 다음 다시 설정하지 않으려는 경우는 드뭅니다.It is rare that you want to set the module state and then not set it back. 대부분의 경우 자신의 모듈 상태를 현재 모듈 상태로 "푸시"한 다음 완료한 후 원래 컨텍스트를 다시 "팝"합니다.Most of the time you want to "push" your own module state as the current one and then, after you are done, "pop" the original context back. 이 작업은 매크로 AFX_MANAGE_STATE 특수 클래스에 AFX_MAINTAIN_STATE의해 수행됩니다.This is done by the macro AFX_MANAGE_STATE and the special class AFX_MAINTAIN_STATE.

CCmdTarget모듈 상태 전환을 지원하기 위한 특수 기능이 있습니다.CCmdTarget has special features for supporting module state switching. 특히, A는 CCmdTarget OLE 자동화 및 OLE COM 진입점에 사용되는 루트 클래스입니다.In particular, a CCmdTarget is the root class used for OLE automation and OLE COM entry points. 시스템에 노출된 다른 진입점과 마찬가지로 이러한 진입점은 올바른 모듈 상태를 설정해야 합니다.Like any other entry point exposed to the system, these entry points must set the correct module state. 주어진 CCmdTarget "올바른" 모듈 상태가 무엇인지 어떻게 알 수 있습니까 대답은 "현재" 모듈 상태가 생성될 때 "기억"하여 나중에 호출될 때 현재 모듈 상태를 "기억" 값으로 설정할 수 있다는 것입니다.How does a given CCmdTarget know what the "correct" module state should be The answer is that it "remembers" what the "current" module state is when it is constructed, such that it can set the current module state to that "remembered" value when it is later called. 따라서 지정된 CCmdTarget 개체가 연관된 모듈 상태는 개체가 생성될 때 현재 상태인 모듈 상태입니다.As a result, the module state that a given CCmdTarget object is associated with is the module state that was current when the object was constructed. INPROC 서버를 로드하고, 개체를 만들고, 해당 메서드를 호출하는 간단한 예제를 예로 들어 보겠습니다.Take a simple example of loading an INPROC server, creating an object, and calling its methods.

  1. DLL은 OLE에서 를 LoadLibrary사용하여 로드됩니다.The DLL is loaded by OLE using LoadLibrary.

  2. RawDllMain을 먼저 호출합니다.RawDllMain is called first. 모듈 상태를 DLL에 대해 알려진 정적 모듈 상태로 설정합니다.It sets the module state to the known static module state for the DLL. 이러한 이유로 RawDllMain DLL에 정적으로 연결됩니다.For this reason RawDllMain is statically linked to the DLL.

  3. 개체와 연결된 클래스 팩터리의 생성자가 호출됩니다.The constructor for the class factory associated with our object is called. COleObjectFactory에서 파생 CCmdTarget 되고 결과적으로, 그것은 모듈 상태 는 인스턴스화 된 기억.COleObjectFactory is derived from CCmdTarget and as a result, it remembers in which module state it was instantiated. 이것은 중요합니다 - 클래스 팩터리에서 개체를 만들라는 메시지가 표시됩니다.This is important — when the class factory is asked to create objects, it knows now what module state to make current.

  4. DllGetClassObject클래스 팩터리를 얻기 위해 호출됩니다.DllGetClassObject is called to obtain the class factory. MFC는 이 모듈과 연결된 클래스 팩터리 목록을 검색하여 반환합니다.MFC searches the class factory list associated with this module and returns it.

  5. COleObjectFactory::XClassFactory2::CreateInstance을 호출합니다.COleObjectFactory::XClassFactory2::CreateInstance is called. 개체를 만들고 반환하기 전에 이 함수는 모듈 상태를 3단계의 현재 상태(인스턴스화시 현재 COleObjectFactory 상태)로 설정합니다.Before creating the object and returning it, this function sets the module state to the module state that was current in step 3 (the one that was current when the COleObjectFactory was instantiated). 이것은 METHOD_PROLOGUE내부에서 수행됩니다.This is done inside of METHOD_PROLOGUE.

  6. 개체를 만들 때 너무 CCmdTarget 파생 이며 동일한 방식으로 COleObjectFactory 활성화 된 모듈 상태를 기억, 그래서이 새 개체를 수행 합니다.When the object is created, it too is a CCmdTarget derivative and in the same way COleObjectFactory remembered which module state was active, so does this new object. 이제 개체는 호출될 때마다 전환할 모듈 상태를 알고 있습니다.Now the object knows which module state to switch to whenever it is called.

  7. 클라이언트는 호출에서 받은 OLE COM 개체의 CoCreateInstance 함수를 호출합니다.The client calls a function on the OLE COM object it received from its CoCreateInstance call. 개체가 호출되면 모듈 METHOD_PROLOGUE 상태를 전환하는 데 COleObjectFactory 사용됩니다.When the object is called it uses METHOD_PROLOGUE to switch the module state just like COleObjectFactory does.

보시다시피 모듈 상태는 생성될 때 개체에서 개체로 전파됩니다.As you can see, the module state is propagated from object to object as they are created. 모듈 상태를 적절하게 설정하는 것이 중요합니다.It is important to have the module state set appropriately. 설정되지 않은 경우 DLL 또는 COM 개체가 호출하는 MFC 응용 프로그램과 제대로 상호 작용하지 않거나 자체 리소스를 찾을 수 없거나 다른 비참한 방법으로 실패할 수 있습니다.If it is not set, your DLL or COM object may interact poorly with an MFC application that is calling it, or may be unable to find its own resources, or may fail in other miserable ways.

특정 종류의 DLL, 특히 "MFC 확장" DLL은 모듈 상태를 RawDllMain 전환하지 않습니다(실제로는 일반적으로 모듈 RawDllMain상태가 없습니다).Note that certain kinds of DLLs, specifically "MFC Extension" DLLs do not switch the module state in their RawDllMain (actually, they usually don't even have a RawDllMain). 이는 실제로 응용 프로그램을 사용하는 응용 프로그램에 있는 것처럼 작동하기 위한 것이기 때문입니다.This is because they are intended to behave "as if" they were actually present in the application that uses them. 실행 중인 응용 프로그램의 일부이며 해당 응용 프로그램의 전역 상태를 수정하려는 의도입니다.They are very much a part of the application that is running and it is their intention to modify that application's global state.

OLE 컨트롤 및 기타 DLL은 매우 다릅니다.OLE Controls and other DLLs are very different. 호출 응용 프로그램의 상태를 수정하지 않으려고 합니다. 호출하는 응용 프로그램은 MFC 응용 프로그램이 아니므로 수정할 상태가 없을 수 있습니다.They do not want to modify the calling application's state; the application that is calling them may not even be an MFC application and so there may be no state to modify. 이것이 모듈 상태 전환이 발명된 이유입니다.This is the reason that module state switching was invented.

DLL에서 대화 상자를 시작하는 함수와 같이 DLL에서 내보낸 함수의 경우 함수의 시작 부분에 다음 코드를 추가해야 합니다.For exported functions from a DLL, such as one that launches a dialog box in your DLL, you need to add the following code to the beginning of the function:

AFX_MANAGE_STATE(AfxGetStaticModuleState())

이렇게 하면 현재 모듈 상태가 AfxGetStaticModuleState에서 현재 범위가 끝날 때까지 반환된 상태로 바꿉니다.This swaps the current module state with the state returned from AfxGetStaticModuleState until the end of the current scope.

AFX_MODULE_STATE 매크로를 사용하지 않으면 DLL의 리소스 문제가 발생합니다.Problems with resources in DLLs will occur if the AFX_MODULE_STATE macro is not used. 기본적으로 MFC는 주 응용 프로그램의 리소스 핸들을 사용하여 리소스 템플릿을 로드합니다.By default, MFC uses the resource handle of the main application to load the resource template. 이 템플릿은 실제로 DLL에 저장됩니다.This template is actually stored in the DLL. 근본 원인은 MFC의 모듈 상태 정보가 AFX_MODULE_STATE 매크로에 의해 전환되지 않았기 때문에 입니다.The root cause is that MFC's module state information has not been switched by the AFX_MODULE_STATE macro. 리소스 핸들은 MFC의 모듈 상태에서 복구됩니다.The resource handle is recovered from MFC's module state. 모듈 상태를 전환하지 않아 잘못된 리소스 핸들이 사용됩니다.Not switching the module state causes the wrong resource handle to be used.

AFX_MODULE_STATE DLL의 모든 함수에 넣을 필요는 없습니다.AFX_MODULE_STATE does not need to be put in every function in the DLL. 예를 들어 InitInstance MFC가 모듈 상태를 자동으로 InitInstance 전환한 다음 반환 후 InitInstance 다시 전환하기 때문에 AFX_MODULE_STATE 않고 응용 프로그램의 MFC 코드에서 호출할 수 있습니다.For example, InitInstance can be called by the MFC code in the application without AFX_MODULE_STATE because MFC automatically shifts the module state before InitInstance and then switches it back after InitInstance returns. 모든 메시지 맵 처리기에서도 마찬가지입니다.The same is true for all message map handlers. 일반 MFC DLL에는 실제로 메시지를 라우팅하기 전에 모듈 상태를 자동으로 전환하는 특수 마스터 창 프로시저가 있습니다.Regular MFC DLLs actually have a special master window procedure that automatically switches the module state before routing any message.

로컬 데이터 처리Process Local Data

Win32s DLL 모델의 난이도가 아니라면 프로세스 로컬 데이터는 큰 문제가 되지 않을 것입니다.Process local data would not be of such great concern had it not been for the difficulty of the Win32s DLL model. Win32s에서 모든 DLL은 여러 응용 프로그램에서 로드된 경우에도 글로벌 데이터를 공유합니다.In Win32s all DLLs share their global data, even when loaded by multiple applications. 이는 각 DLL이 DLL에 첨부되는 각 프로세스에서 데이터 공간의 별도 복사본을 얻는 "실제" Win32 DLL 데이터 모델과는 매우 다릅니다.This is very different from the "real" Win32 DLL data model, where each DLL gets a separate copy of its data space in each process that attaches to the DLL. 복잡성을 더하기 위해 Win32s DLL의 힙에 할당된 데이터는 실제로 특정 프로세스입니다(적어도 소유권이 가는 한).To add to the complexity, data allocated on the heap in a Win32s DLL is in fact process specific (at least as far as ownership goes). 다음 데이터 및 코드를 고려하십시오.Consider the following data and code:

static CString strGlobal; // at file scope

__declspec(dllexport)
void SetGlobalString(LPCTSTR lpsz)
{
    strGlobal = lpsz;
}

__declspec(dllexport)
void GetGlobalString(LPCTSTR lpsz, size_t cb)
{
    StringCbCopy(lpsz, cb, strGlobal);
}

위의 코드가 DLL에 있고 DLL이 두 프로세스 A와 B에 의해 로드되는 경우 어떻게 되는지 고려하십시오(실제로 동일한 응용 프로그램의 두 인스턴스일 수 있음).Consider what happens if the above code is in located in a DLL and that DLL is loaded by two processes A and B (it could, in fact, be two instances of the same application). 호출합니다. SetGlobalString("Hello from A")A calls SetGlobalString("Hello from A"). 결과적으로 프로세스 CString A. CString 자체가 전역이며 A와 B 모두에 표시된다는 점을 염두에 두어야 합니다. 이제 B가 호출합니다. GetGlobalString(sz, sizeof(sz))As a result, memory is allocated for the CString data in the context of process A. Keep in mind that the CString itself is global and is visible to both A and B. Now B calls GetGlobalString(sz, sizeof(sz)). B는 A가 설정한 데이터를 볼 수 있습니다.B will be able to see the data that A set. Win32s는 Win32와 같은 프로세스 간에 보호를 제공하지 않기 때문입니다.This is because Win32s offers no protection between processes like Win32 does. 이것이 첫 번째 문제입니다. 대부분의 경우 하나의 응용 프로그램이 다른 응용 프로그램에서 소유한 것으로 간주되는 전역 데이터에 영향을 주는 것은 바람직하지 않습니다.That is the first problem; in many cases it is not desirable to have one application affect global data that is considered to be owned by a different application.

추가 문제도 있습니다.There are additional problems as well. 이제 A가 종료되었다고 가정해 봅시다.Let's say that A now exits. A가 종료되면 ' 문자열에서strGlobal사용하는 메모리를 시스템에 사용할 수 있게 됩니다.When A exits, the memory used by the 'strGlobal' string is made available for the system — that is, all memory allocated by process A is freed automatically by the operating system. CString 소멸자가 호출되기 때문에 해제되지 않습니다. 아직 호출되지 않았습니다.It is not freed because the CString destructor is being called; it hasn't been called yet. 할당된 응용 프로그램이 장면을 떠났기 때문에 해제됩니다.It is freed simply because the application which allocated it has left the scene. 이제 B가 GetGlobalString(sz, sizeof(sz))호출되면 유효한 데이터가 얻을 수 없습니다.Now if B called GetGlobalString(sz, sizeof(sz)), it may not get valid data. 다른 응용 프로그램에서 는 다른 용도로 해당 메모리를 사용했을 수 있습니다.Some other application may have used that memory for something else.

분명히 문제가 있습니다.Clearly a problem exists. MFC 3.x는 TLS(스레드 로컬 저장소)라는 기술을 사용했습니다.MFC 3.x used a technique called thread-local storage (TLS). MFC 3.x는 Win32s에서 실제로 프로세스 로컬 저장소 인덱스로 작동하는 TLS 인덱스를 할당합니다.MFC 3.x would allocate a TLS index that under Win32s really acts as a process-local storage index, even though it is not called that and then would reference all data based on that TLS index. 이는 Win32에 스레드 로컬 데이터를 저장하는 데 사용된 TLS 인덱스와 유사합니다(해당 주제에 대한 자세한 내용은 아래 참조).This is similar to the TLS index that was used to store thread-local data on Win32 (see below for more information on that subject). 이로 인해 모든 MFC DLL은 프로세스당 적어도 두 개의 TLS 인덱스를 활용하게 되었습니다.This caused every MFC DLL to utilize at least two TLS indices per process. 많은 OLE 제어 DDL(OCX)을 로드하는 것을 고려할 때 TLS 지수가 빠르게 소진됩니다(사용 가능한 경우 는 64개만 있음).When you account for loading many OLE Control DLLs (OCXs), you quickly run out of TLS indices (there are only 64 available). 또한 MFC는 이 모든 데이터를 하나의 구조로 한 곳에 배치해야 했습니다.In addition, MFC had to place all this data in one place, in a single structure. 그것은 매우 확장되지 않았으며 TLS 지수의 사용과 관련하여 이상적이지 않았습니다.It was not very extensible and was not ideal with regard to its use of TLS indices.

MFC 4.x는 로컬 프로세스해야 하는 데이터를 "래핑"할 수 있는 클래스 템플릿 집합으로 이 주소를 해결합니다.MFC 4.x addresses this with a set of class templates you can "wrap" around the data that should be process local. 예를 들어 위에서 언급한 문제는 다음과 같이 작성하여 해결할 수 있습니다.For example, the problem mentioned above could be fixed by writing:

struct CMyGlobalData : public CNoTrackObject
{
    CString strGlobal;
};
CProcessLocal<CMyGlobalData> globalData;

__declspec(dllexport)
void SetGlobalString(LPCTSTR lpsz)
{
    globalData->strGlobal = lpsz;
}

__declspec(dllexport)
void GetGlobalString(LPCTSTR lpsz, size_t cb)
{
    StringCbCopy(lpsz, cb, globalData->strGlobal);
}

MFC는 이를 두 단계로 구현합니다.MFC implements this in two steps. 첫째, Win32 __Tls* __ API(TlsAlloc,TlsAlloc TlsSetValue, TlsGetValue등)의 상단에 는 프로세스당 두 개의 TLS 인덱스만 사용하는 계층이 있습니다.First, there is a layer on top of the Win32 Tls* APIs (TlsAlloc, TlsSetValue, TlsGetValue, etc.) which use only two TLS indexes per process, no matter how many DLLs you have. 둘째, CProcessLocal 이 데이터에 액세스하기 위해 템플릿이 제공됩니다.Second, the CProcessLocal template is provided to access this data. 위에서 볼 수 있는 직관적인 구문을 허용하는 연산자 > 재정의합니다.It overrides operator-> which is what allows the intuitive syntax you see above. 래핑된 모든 CProcessLocal 개체는 에서 CNoTrackObject파생되어야 합니다.All objects that are wrapped by CProcessLocal must be derived from CNoTrackObject. CNoTrackObject프로세스가 종료될 때 MFC가 프로세스 로컬 개체를 자동으로 파괴할 수 있도록 하위 수준 할당자(LocalAlloc/LocalFree)와가상 소멸자(virtual destroyctor)를 제공합니다.LocalAllocCNoTrackObject provides a lower-level allocator (LocalAlloc/LocalFree) and a virtual destructor such that MFC can automatically destroy the process local objects when the process is terminated. 이러한 개체는 추가 정리가 필요한 경우 사용자 지정 소멸자가 있을 수 있습니다.Such objects can have a custom destructor if additional cleanup is required. 컴파일러는 포함된 CString 개체를 파괴하기 위해 기본 소멸자를 생성하므로 위의 예제에서는 하나를 필요로 하지 않습니다.The above example doesn't require one, since the compiler will generate a default destructor to destroy the embedded CString object.

이 접근 방식에는 다른 흥미로운 이점이 있습니다.There are other interesting advantages to this approach. 모든 CProcessLocal 개체가 자동으로 소멸될 뿐만 아니라 필요할 때까지 생성되지 않습니다.Not only are all CProcessLocal objects destroyed automatically, they are not constructed until they are needed. CProcessLocal::operator->을 참조하면 연결된 개체가 호출될 때 인스턴스화되고 더 빨리 인스턴스화되지 않습니다.CProcessLocal::operator-> will instantiate the associated object the first time it is called, and no sooner. 위의 예에서 ' 문자열은strGlobal처음 호출될 때까지 생성되지 SetGlobalString 않거나 GetGlobalString 호출되지 않음을 의미합니다.In the example above, that means that the 'strGlobal' string will not be constructed until the first time SetGlobalString or GetGlobalString is called. 경우에 따라 DLL 시작 시간을 줄이는 데 도움이 될 수 있습니다.In some instances, this can help decrease DLL startup time.

스레드 로컬 데이터Thread Local Data

로컬 데이터를 처리하는 것과 마찬가지로 데이터가 지정된 스레드에 로컬이어야 하는 경우 스레드 로컬 데이터가 사용됩니다.Similar to process local data, thread local data is used when the data must be local to a given thread. 즉, 해당 데이터에 액세스하는 각 스레드에 대해 별도의 데이터 인스턴스가 필요합니다.That is, you need a separate instance of the data for each thread that accesses that data. 이것은 광범위한 동기화 메커니즘 대신 여러 번 사용할 수 있습니다.This can many times be used in lieu of extensive synchronization mechanisms. 여러 스레드에서 데이터를 공유할 필요가 없는 경우 이러한 메커니즘은 비용이 많이 들고 불필요할 수 있습니다.If the data does not need to be shared by multiple threads, such mechanisms can be expensive and unnecessary. 위의 샘플과 CString 마찬가지로 개체가 있다고 가정합니다.Suppose we had a CString object (much like the sample above). 템플릿으로 래핑하여 스레드를 로컬로 CThreadLocal 만들 수 있습니다.We can make it thread local by wrapping it with a CThreadLocal template:

struct CMyThreadData : public CNoTrackObject
{
    CString strThread;
};
CThreadLocal<CMyThreadData> threadData;

void MakeRandomString()
{
    // a kind of card shuffle (not a great one)
    CString& str = threadData->strThread;
    str.Empty();
    while (str.GetLength() != 52)
    {
        unsigned int randomNumber;
        errno_t randErr;
        randErr = rand_s(&randomNumber);

        if (randErr == 0)
        {
            TCHAR ch = randomNumber % 52 + 1;
            if (str.Find(ch) <0)
            str += ch; // not found, add it
        }
    }
}

서로 MakeRandomString 다른 두 스레드에서 호출된 경우 각 스레드는 다른 스레드를 방해하지 않고 서로 다른 방식으로 문자열을 "섞는"것입니다.If MakeRandomString was called from two different threads, each would "shuffle" the string in different ways without interfering with the other. 이는 실제로 하나의 전역 strThread 인스턴스가 아닌 스레드당 인스턴스가 있기 때문입니다.This is because there is actually a strThread instance per thread instead of just one global instance.

루프 반복당 한 번이 CString 아니라 참조를 사용하여 주소를 한 번 캡처하는 방법을 설명합니다.Note how a reference is used to capture the CString address once instead of once per loop iteration. 루프 코드는 threadData->strThread 어디에지에 ''로str작성되었을 수 있지만 코드는 실행 속도가 훨씬 느려집니다.The loop code could have been written with threadData->strThread everywhere 'str' is used, but the code would be much slower in execution. 이러한 참조가 루프에서 발생할 때 데이터에 대한 참조를 캐시하는 것이 가장 좋습니다.It is best to cache a reference to the data when such references occur in loops.

클래스 CThreadLocal 템플릿은 CProcessLocal 수행하는 것과 동일한 구현 기술과 동일한 메커니즘을 사용합니다.The CThreadLocal class template uses the same mechanisms that CProcessLocal does and the same implementation techniques.

참고 항목See also

숫자별 기술 노트Technical Notes by Number
범주별 기술 참고 사항Technical Notes by Category