기술 자료

Unity Engine

Unity Engine

잡 생성 및 실행

Create and schedule a job.
읽는 시간 5분최근 업데이트: 한 달 전

잡을 만들고 성공적으로 실행하려면 다음을 수행해야 합니다.
  • 잡 생성:
    IJob
    인터페이스를 구현합니다.
  • 잡 예약: 잡에서
    Schedule
    메서드를 호출합니다.
  • 잡이 완료될 때까지 대기합니다. 잡이 이미 완료된 경우 즉시 반환되며, 데이터에 액세스하고 싶을 때 해당 잡에서
    Complete
    메서드를 호출하면 됩니다.

잡 생성

Unity에서 잡을 만들려면
IJob
인터페이스를 구현해야 합니다.
IJob
을 사용하면 실행 중인 다른 잡과 병렬로 실행되는 단일 잡을 예약할 수 있습니다.
IJob
에는 하나의 필수 메서드가 있습니다. 워커 스레드가 해당 잡을 실행할 때마다 Unity에서 호출하는
Execute
입니다.
또한 잡을 생성할 때 다른 메서드에서 해당 잡을 참조할 때 사용해야 하는
JobHandle
을 생성할 수도 있습니다.
중요: 잡 내에서 읽기 전용이 아니거나 변경 가능한 정적 데이터에 액세스하지 못하도록 보호하는 기능은 없습니다. 이러한 데이터에 액세스하면 모든 안전 시스템을 우회하여 애플리케이션 또는 Unity 에디터에 크래시가 발생할 수 있습니다.
Unity가 실행되면 잡 시스템이 예약된 잡 데이터의 사본을 생성하여 둘 이상의 스레드가 동일한 데이터를 읽거나 쓰는 것을 방지합니다. 잡이 완료된 후에는
NativeContainer
에 기록된 데이터에만 액세스할 수 있습니다. 이는 잡에서 사용하는
NativeContainer
의 사본과 원본
NativeContainer
오브젝트가 모두 동일한 메모리를 가리키기 때문입니다. 자세한 내용은 스레드 세이프 유형 기술 자료를 참조하십시오.
잡 시스템이 잡 대기열에서 잡을 선택하면 단일 스레드에서
Execute
메서드를 한 번 실행합니다. 일반적으로 잡 시스템은 백그라운드 스레드에서 잡을 실행하지만 대기 상태가 되면 메인 스레드를 선택할 수 있습니다. 따라서 한 프레임에서 완료할 수 있도록 잡을 설계해야 합니다.

잡 예약

잡을 예약하려면
Schedule
을 호출해야 합니다. 그러면 잡이 잡 대기열에 추가되고, 잡 시스템은 모든 해당 종속성이 완료되면(있는 경우) 잡을 실행하기 시작합니다. 일단 예약된 작업은 인터럽트할 수 없습니다. 메인 스레드에서는
Schedule
만 호출할 수 있습니다.
팁: 잡에는
Schedule
대신 사용하여 메인 스레드에서 작업을 즉시 실행할 수 있는
Run
메서드가 있습니다. 이 메서드는 디버깅 용도로 사용할 수 있습니다.

잡 완료

Schedule
을 호출하고 잡 시스템이 잡을 실행한 후에는
JobHandle
에서
Complete
메서드를 호출하여 잡의 데이터에 액세스할 수 있습니다. 코드에서 최대한 늦게
Complete
을 호출하는 것이 좋습니다.
Complete
를 호출하면 메인 스레드가 잡에서 사용 중이던
NativeContainer
인스턴스에 안전하게 액세스할 수 있습니다. 또한
Complete
를 호출하면 안전 시스템에서 상태가 클린업됩니다. 그렇게 하지 않으면 메모리 누수가 발생합니다.

잡 예제

다음은 두 개의 부동 소수점 값을 더하는 잡의 예시입니다. 아래의 잡은
IJob
을 구현하고,
NativeArray
를 사용하여 잡의 결과를 얻으며, 그 안에서 잡의 구현과 함께
Execute
메서드를 사용합니다.
using UnityEngine;using Unity.Collections;using Unity.Jobs;// Job adding two floating point values togetherpublic struct MyJob : IJob{ public float a; public float b; public NativeArray<float> result; public void Execute() { result[0] = a + b; }}
다음 예시는
MyJob
잡을 기반으로 메인 스레드에서 잡을 예약합니다.
using UnityEngine;using Unity.Collections;using Unity.Jobs;public class MyScheduledJob : MonoBehaviour{ // Create a native array of a single float to store the result. Using a // NativeArray is the only way you can get the results of the job, whether // you're getting one value or an array of values. NativeArray<float> result; // Create a JobHandle for the job JobHandle handle; // Set up the job public struct MyJob : IJob { public float a; public float b; public NativeArray<float> result; public void Execute() { result[0] = a + b; } } // Update is called once per frame void Update() { // Set up the job data result = new NativeArray<float>(1, Allocator.TempJob); MyJob jobData = new MyJob { a = 10, b = 10, result = result }; // Schedule the job handle = jobData.Schedule(); } private void LateUpdate() { // Sometime later in the frame, wait for the job to complete before accessing the results. handle.Complete(); // All copies of the NativeArray point to the same memory, you can access the result in "your" copy of the NativeArray // float aPlusB = result[0]; // Free the memory allocated by the result array result.Dispose(); }}

베스트 프랙티스 예약 및 완료

필요한 데이터가 확보되는 즉시 잡에서
Schedule
을 호출하고 결과가 필요할 때까지
Complete
를 호출하지 않는 것이 좋습니다.
중요도가 상대적으로 낮은 잡은 중요도가 높은 잡과 경쟁하지 않는 프레임의 일부에 예약할 수 있습니다.
예를 들어 한 프레임의 끝과 다음 프레임의 시작 사이에 실행 중인 잡이 없고 한 프레임의 지연이 허용되는 기간이 있는 경우, 프레임이 끝날 때 잡을 예약하고 다음 프레임에서 그 결과를 사용할 수 있습니다. 또는 다른 잡으로 인해 해당 전환 기간이 포화 상태이고 프레임의 다른 곳에 활용도가 낮은 기간이 있는 경우, 대신 해당 기간에 잡을 예약하는 것이 더 효율적입니다.
프로파일러를 사용하여 Unity가 작업이 완료되기를 기다리는 위치를 확인할 수도 있습니다. 메인 스레드의
WaitForJobGroupID
마커가 이를 나타냅니다. 이 마커는 해결해야 하는 데이터 종속성이 어딘가에 도입되었음을 의미할 수 있습니다.
JobHandle.Complete
를 찾아 메인 스레드를 대기시키는 데이터 종속성이 있는 위치를 추적하십시오.

오래 실행되는 잡 사용 피하기

스레드와는 달리 잡은 실행을 양보하지 않습니다. 일단 잡이 시작되면 해당 잡을 맡은 워커 스레드는 다른 잡을 실행하기 전에 해당 잡부터 완료합니다. 따라서 비교적 오래 실행해야 하는 잡은 그대로 제출하는 대신 상호 의존하는 작은 잡들으로 분할하는 것이 바람직합니다.
잡 시스템은 일반적으로 여러 개의 잡 종속성 체인을 실행하므로 오래 실행해야 하는 잡을 여러 개로 나누면 여러 개의 잡 체인이 진행될 수 있습니다. 대신 잡 시스템이 오래 실행되는 잡들로 채워지면 모든 워커 스레드가 완전히 소모되어 독립적인 잡의 실행이 차단될 수 있습니다. 이렇게 하면 메인 스레드가 명시적으로 대기하는 중요한 잡의 완료 시간이 지연되어 메인 스레드에 불필요한 지연이 발생할 수 있습니다.
특히 오래 실행되는
IJobParallelFor
잡은 잡 배치 크기에 따라 최대한 많은 워커 스레드에서 실행하려고 의도적으로 시도하기 때문에 잡 시스템에 부정적인 영향을 미칩니다. 긴 병렬 잡들을 분할할 수 없는 경우, 잡을 예약할 때 잡의 배치 크기를 늘려서 오래 실행되는 잡을 선택하는 워커의 수를 제한하는 것이 좋습니다.
MyParallelJob jobData = new MyParallelJob();jobData.Data = someData; jobData.Result = someArray; // Use half the available worker threads, clamped to a minimum of 1 worker threadconst int numBatches = Math.Max(1, JobsUtility.JobWorkerCount / 2); const int totalItems = someArray.Length;const int batchSize = totalItems / numBatches;// Schedule the job with one Execute per index in the results array and batchSize items per processing batchJobHandle handle = jobData.Schedule(result.Length, totalItems, batchSize);

추가 리소스