Version: 2023.1
언어: 한국어
커스텀 NativeContainer 예제
잡 종속성

잡 생성 및 실행

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

  • 잡 생성: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);

추가 리소스

커스텀 NativeContainer 예제
잡 종속성