Version: Unity 6.0 (6000.0)
언어 : 한국어
잡 개요
잡 종속성

잡 생성 및 실행

잡을 만들고 성공적으로 실행하려면 다음을 수행해야 합니다.

  • 잡 생성: 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 together
public 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 thread
const 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 batch
JobHandle handle = jobData.Schedule(result.Length, totalItems, batchSize);

추가 리소스

잡 개요
잡 종속성