DirectX 11 응용 프로그램에서 멀티스레딩(Multi-threading)을 사용하려면 응용 프로그램이 하나 이상의 스레드를 만들고 실행을 관리해야 한다. 여기에는 일반적으로 태스크를 실행하는데 사용할 수 있는 스레드 모음인 스레드 풀을 생성하는 작업이 포함된다. 그런 다음 응용 프로그램은 스레드 풀에 작업을 할당하고 사용 가능한 스레드 중 하나에서 작업이 실행되도록 예약한다.

 

목차

     

     


     

     

    Thread

     

    DirectX 11에서 멀티스레딩(Multi-threading)은 여러 CPU 코어에서 동시에 작업을 실행할 수 있게 함으로써 성능을 향상시키고 대기 시간을 줄이는 데 사용될 수 있다. 이 기능은 병렬화가 가능한 렌더링과 같은 작업에 특히 유용하다.

    DirectX 11 응용 프로그램에서 멀티스레딩(Multi-threading)을 사용하려면 응용 프로그램이 하나 이상의 스레드를 만들고 실행을 관리해야 한다. 여기에는 일반적으로 태스크를 실행하는데 사용할 수 있는 스레드 모음인 스레드 풀을 생성하는 작업이 포함된다. 그런 다음 응용 프로그램은 스레드 풀에 작업을 할당하고 사용 가능한 스레드 중 하나에서 작업이 실행되도록 예약한다.

    DirectX 11 응용 프로그램에서 멀티스레딩(Multi-threading)을 사용하는 경우 메모리 및 데이터 구조와 같은 공유 리소스에 대한 액세스가 올바르게 동기화되도록 하는 것이 중요하다. 이는 한 번에 하나의 스레드만 공유 리소스에 액세스할 수 있도록 보장하는 뮤트크세스(mutexes) 및 세마포어(semaphores) 같은 동기화 기본 요소를 사용한다.

    DirectX 11 응용 프로그램에서 멀티스레딩을 사용할 때 고려해야 할 또 다른 중요한 사항은 스레드 친화성이다. 이는 컨텍스트 스위칭 오버헤드를 최소화하고 캐시 인접성을 개선하기 위해 특정 CPU 코어에 스레드를 할당하는 것을 의미한다. 애플리케이션은 특정 CPU 코어에 스레드를 할당하여 스레드 생성 및 파괴와 관련된 오버헤드를 줄이고 전반적인 성능을 향상시킬 수 있다.

    전반적으로 멀티스레딩은 DirectX 11 애플리케이션의 성능을 향상시키는 강력한 도구가 될 수 있지만 효과적으로 사용되도록 하려면 신중한 관리와 동기화가 필요하다.

     

    더보기

    In DirectX 11, multithreading can be used to improve performance and reduce latency by allowing tasks to be executed concurrently on multiple CPU cores. This can be particularly useful for tasks such as rendering, which can be highly parallelizable.

     

    To use multithreading in a DirectX 11 application, the application must create one or more threads and manage their execution. This typically involves creating a thread pool, which is a collection of threads that can be used to execute tasks. The application then assigns tasks to the thread pool, which schedules them for execution on one of the available threads.

     

    When using multithreading in a DirectX 11 application, it's important to ensure that access to shared resources such as memory and data structures is properly synchronized. This can be achieved using synchronization primitives such as mutexes and semaphores, which ensure that only one thread can access a shared resource at a time.

     

    Another important consideration when using multithreading in a DirectX 11 application is thread affinity. This refers to the assignment of threads to specific CPU cores to minimize context switching overhead and improve cache locality. By assigning threads to specific CPU cores, the application can reduce the overhead associated with thread creation and destruction and improve overall performance.

    Overall, multithreading can be a powerful tool for improving performance in DirectX 11 applications, but it requires careful management and synchronization to ensure that it is used effectively.

     


     

    예제 코드

     

    예제 코드

    더보기
    #include <windows.h>
    #include <d3d11.h>
    #include <thread>
    
    void RenderFrame(ID3D11DeviceContext* pDeviceContext)
    {
        // Render a frame here...
    }
    
    int main()
    {
        // Initialize DirectX 11 here...
    
        // Create a thread pool with four threads
        const int NumThreads = 4;
        std::thread threads[NumThreads];
    
        // Assign rendering tasks to the thread pool
        for (int i = 0; i < NumThreads; i++)
        {
            threads[i] = std::thread(RenderFrame, pDeviceContext);
        }
    
        // Wait for all tasks to complete
        for (int i = 0; i < NumThreads; i++)
        {
            threads[i].join();
        }
    
        return 0;
    }

    이 예시에서는 4개의 스레드로 스레드 풀을 만들고 std::thread 클래스를 사용하여 각 스레드에 렌더링 작업을 할당한다. 각 스레드는 주어진 ID3D11DeviceContext 개체를 사용하여 단일 프레임을 렌더링하는 RenderFrame 함수를 실행한다.

    모든 작업이 스레드 풀에 할당된 후에는 조인 기능을 사용하여 각 스레드가 완료될 때까지 기다린다. 이렇게 하면 응용 프로그램이 종료되기 전에 모든 렌더링 작업이 완료된다.

    이것은 단순화된 예이며 오류 처리 또는 동기화 기본 요소는 포함하지 않는다. 실제 응용 프로그램에서는 그래픽 장치 및 메모리 버퍼와 같은 공유 리소스에 대한 액세스가 뮤트크세스 또는 기타 동기화 기본 요소를 사용하여 올바르게 동기화되었는지 확인해야 한다.

    더보기

    In this example, we create a thread pool with four threads and assign rendering tasks to each thread using the std::thread class. Each thread executes the RenderFrame function, which renders a single frame using the given ID3D11DeviceContext object.

     

    After all tasks have been assigned to the thread pool, we wait for each thread to complete using the join function. This ensures that all rendering tasks have completed before the application exits.

     

    Note that this is a simplified example and does not include error handling or synchronization primitives. In a real-world application, you would need to ensure that access to shared resources such as the graphics device and memory buffers is properly synchronized using mutexes or other synchronization primitives.

     

     

     


     

    Thread Demo

     

    ThreadDemo.h

    더보기
    #pragma once
    #include "Systems/IExecute.h"
    
    class ThreadDemo : public IExecute
    {
    public:
    	virtual void Initialize() override;
    	virtual void Ready() override {}
    	virtual void Destroy() override {}
    	virtual void Update() override;
    	virtual void PreRender() override {}
    	virtual void Render() override;
    	virtual void PostRender() override {}
    	virtual void ResizeScreen() override {}
    
    private:
    	void Loop();
    
    	void Function();
    	
    	void MultiThread();
    	void MultiThread1();
    	void MultiThread2();
    
    	void Join();
    };

     

     

    ThreadDemo.cpp

    더보기
    #include "stdafx.h"
    #include "ThreadDemo.h"
    
    void ThreadDemo::Initialize()
    {
    	//Loop();
    
    	//function<void()> f = bind(&ThreadDemo::Function, this);
    	//f();
    
    	//MultiThread();
    
    	Join();
    }
    
    void ThreadDemo::Update()
    {
    
    }
    
    void ThreadDemo::Render()
    {
    	
    }
    
    void ThreadDemo::Loop()
    {
    	for (int i = 0; i < 100; i++)
    		printf("1 : %d\n", i);
    	printf("반복문1 종료\n");
    
    	for (int i = 0; i < 100; i++)
    		printf("2 : %d\n", i);
    	printf("반복문2 종료\n");
    }
    
    void ThreadDemo::Function()
    {
    	printf("함수 포인터 호출\n");
    }
    
    void ThreadDemo::MultiThread()
    {
    	thread t(bind(&ThreadDemo::MultiThread1, this));
    	thread t2(bind(&ThreadDemo::MultiThread2, this));
    
    	t2.join(); //t2가 종료될 때까지 대기. 다음으로 넘어가지 않음.
    	printf("t2.join\n");
    
    	t.join(); //t가 종료될 때까지 대기. 다음으로 넘어가지 않음.
    	printf("t.join\n");
    }
    
    void ThreadDemo::MultiThread1()
    {
    	for (int i = 0; i < 100; i++)
    		printf("1 : %d\n", i);
    	printf("반복문1 종료\n");
    }
    
    void ThreadDemo::MultiThread2()
    {
    	for (int i = 0; i < 100; i++)
    		printf("2 : %d\n", i);
    	printf("반복문2 종료\n");
    }
    
    void ThreadDemo::Join()
    {
    	thread t([]()
    	{
    		for (int i = 0; i < 100; i++)
    			printf("1 : %d\n", i);
    		printf("반복문1 종료\n");
    	});
    
    	thread t2([]()
    	{
    		int a = 0;
    		while (true) 
    		{
    			a++;
    			printf("A : %d\n", a);
    
    			Sleep(100);
    
    			if (a > 30)
    				break;
    		}
    	});
    
    	printf("멀티 쓰레드 시작\n");
    
    	t2.join();
    	printf("t2 join\n");
    
    	t.join();
    	printf("t.join\n");
    }

    join()

    • Thead가 종료되기 위해서는 join()이 필요하다.
    • 종료될 때까지 대기한다. 종료될 때까지 다음으로 넘어가지 않는다.
    • 순서를 유의하여 사용해야 한다. 

     


     

    실행화면

     

     

    반복문 1이 종료되었음에도 불구하고 t는 마무리되지 않았다. t2.join이 t1.join보다 앞에 있으므로, t2가 마무리될 때까지 기다렸다가 t가 종료된다.