


using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
public class JobTest : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
NativeArray<int> array = new NativeArray<int>(1, Allocator.TempJob); //Temp,TempJob,Presistent 을 자주 사용. Temp는 1 프레임 내에 모든 일이 결정, TempJob은 Job에서 사용할 것이다. 4프레임 내에 결정. Presistent는 쭉 사용 .
SimpleJob job = new SimpleJob //심플잡을 인스턴스화
{
a = 1,
b = 2,
result = array //언로케이션을 로케이션으로 해줘야 함.
};
JobHandle handle = job.Schedule(); //잡을 스케줄에 넣음 -> 멀티쓰레드로 만듦. handle이 모두 드,ㄹ고 있음.
handle.Complete(); //handle이 들고있는 잡이 끝날 때까지 기다린다. 즉, Execute()가 끝날 때까지 기다린다. (handle에 묶여있는 Job들의 업무가 끝나길 기다린다.)
//Debug.Log(job.c);
Debug.Log(job.result[0]);
array.Dispose(); //free 할 때는 Dis
}
public struct SimpleJob : IJob
{
//Job이라는 쓰레드를 만들기 위해 구조체를 사용한다. 또한, IJob 인터페이스를 구현하게 한다.
public int a;
public int b;
public int c;
public NativeArray<int> result;
public void Execute() //IJob 인터페이스의 Execute()을 구현해야 함.
{
// c = a + b;
result[0] = a + b;
}
}
}








NativeArray<int> array = new NativeArray<int>(1, Allocator.TempJob);
(1, Allocator.TempJob); 크기가 1인 배열 생성 , 4프레임 동안 메모리가 유지됨.
즉, 이 배열은 잡(Job) 간의 데이터 교환을 위해 공유된다.
2️⃣ SimpleJob 생성 및 초기화 : 구조체의 인스턴스화 (객체를 생성)
SimpleJob job = new SimpleJob
{
a = 1,
b = 2,
result = array
};
result = array: 연산 결과를 저장할 NativeArray를 연결한다.
3️⃣ Job 스케줄링 : Job의 작업을 스케줄에 등록한다.
JobHandle handle = job.Schedule();
유니티는 이 작업을 Worker Thread로 전송한다. 메인 스레드는 계속 실행 할 수 있으며, JobHandle의 객체인 handle에 작업의 상태를 저장한다.
4️⃣ 작업 완료 대기 : 모든 Job의 작업이 끝날 때 까지 대기한다.
handle.Complete();
IJob 인터페이스를 구현한 SimpleJob 구조체의 Execute메서드가 완료 될 때까지 기다린 후에 다음 코드를 실행한다.
5️⃣ 결과 확인
Debug.Log(job.result[0]);
array.Dispose();
List.Clear() // 이 코드를 사용하여 기존 리스트를 초기하고 , 새로운 리스트를 할당하지 않는 방식으로 최적화 할 수 있다.
public struct SimpleJob : IJob
{
//Job이라는 쓰레드를 만들기 위해 구조체를 사용한다. 또한, IJob 인터페이스를 구현하게 한다.
public int a;
public int b;
public int c;
public void Execute() //IJob 인터페이스의 Execute()을 구현해야 함.
{
c = a + b;
}
}



JobHandle handle = job.Schedule();
// 단일 작업 실행한다.
JobHandle handle = job.ScheduleParallel (array.Length, 64 , defalut); //64개 단위로 작업 분할
//예를 들어 , 작업 크기(배열의 크기)가 1000일 경우 64개로 나눠서 총 16개의 스레드가 실행된다.
JobHandle handle = job.ScheduleParallel (array.Length, 64 , defalut); //64개 단위로 작업 분할
//예를 들어 , 작업 크기(배열의 크기)가 1000일 경우 64개로 나눠서 총 16개의 스레드가 실행된다.
JobHandle handle = job.ScheduleParallel (array.Length, 64 , defalut);
handle.Complete(); // Complete()를 호출하면 모든 작업이 끝난 후, 결과를 메인 스레드에서 안전하게 통합하거나 읽을 수 있게 됩니다.
NativeArray<int> array = new NativeArray<int>(100, Allocator.TempJob);
array.Dispose(); //위 내용 실행.
public class JobTest : MonoBehaviour
{
void Start()
{
// 🔥 1. NativeArray 생성
NativeArray<int> array = new NativeArray<int>(1, Allocator.TempJob);
}
}
public struct SimpleJob : IJob
{
public int a;
public int b;
// NativeArray가 결과를 저장하는 용도로 사용됨 (클래스의 array와 연결됨)
public NativeArray<int> result;
public void Execute()
{
// Execute()는 IJob 인터페이스의 메서드
result[0] = a + b; // a + b의 결과를 result 배열에 기록
}
}
public class JobTest : MonoBehaviour
{
void Start()
{
// 생략
SimpleJob job = new SimpleJob
{
a = 1,
b = 2,
result = array // 📌 "array"라는 로케이터를 "result"라는 언로케이터에 연결
};
// 생략
}
}
public NativeArray<int> result;

using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
public class JobTest : MonoBehaviour
{
void Start()
{
NativeArray<int> array = new NativeArray<int>(1, Allocator.TempJob);
NativeArray<int> array2 = new NativeArray<int>(1, Allocator.TempJob); //1. 생성
SimpleJob job = new SimpleJob
{
a = 1,
b = 2,
result = array
};
SimpleJob job2 = new SimpleJob //2. 인스턴스화
{
a = 1,
b = 2,
result = array2
};
JobHandle handle = job.Schedule();
JobHandle handle2 = job2.Schedule(handle); //3. handle에 들어있는 Job의 업무를 모두 끝내고 실행
handle2.Complete(); // 최종적으로 handle.Complete()가 아닌 handle2.Complete(); 해서 handle
Debug.Log(job.result[0]);
array.Dispose(); //free 할 때는 Dis
array2.Dispose(); //free 할 때는 Dis
}
public struct SimpleJob : IJob
{
//Job이라는 쓰레드를 만들기 위해 구조체를 사용한다. 또한, IJob 인터페이스를 구현하게 한다.
public int a;
public int b;
public int c;
public NativeArray<int> result;
public void Execute() //IJob 인터페이스의 Execute()을 구현해야 함.
{
// c = a + b;
result[0] = a + b;
}
}
}
Main Thread
↓
1️⃣ Job1 (SimpleJob) - 스케줄링(handle) → Worker Thread 1 실행JobHandle handle = job.Schedule(); //WorkerThread가 Job의 Execute()을 수행↓ (WorkerThread가 Job의 작업을 완료 후 )
2️⃣ Job2 (SimpleJob) - 스케줄링(handle2) → Worker Thread 2 실행JobHandle handle2 = job2.Schedule(handle); //WorkerThread가 Job2의 Execute()을 수행↓
3️⃣ handle2.Complete() - 메인 스레드는 Job, Job2의 모든 작업 완료 대기handle2.Complete(); //Worker Thread의 작업 결과를 메인 스레드로 통합된다.↓
4️⃣ Debug.Log() - Job1 결과 출력, array와 array2의 메모리 해제array.Dispose(); //free 할 때는 Dis array2.Dispose(); //free 할 때는 Dis
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
public class JobTest : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
NativeReference<int> reference = new NativeReference<int>(Allocator.TempJob);
NativeReference<int> reference2 = new NativeReference<int>(Allocator.TempJob);
SimpleJob job = new SimpleJob //심플잡을 인스턴스화
{
a = 1,
b = 2,
result = reference //언로케이션을 로케이션으로 해줘야 함.
};
SimpleJob job2 = new SimpleJob //심플잡을 인스턴스화
{
a = 2,
b = 3,
result = reference2 //언로케이션을 로케이션으로 해줘야 함.
};
JobHandle handle = job.Schedule(); //잡을 스케줄에 넣음 -> 멀티쓰레드로 만듦. handle이 모두 드,ㄹ고 있음.
JobHandle handle2 = job2.Schedule(handle);
handle2.Complete();
//Debug.Log(job.c);
Debug.Log(job.result.Value);
Debug.Log(job2.result.Value);
reference.Dispose(); //free 할 때는 Dis
reference2.Dispose(); //free 할 때는 Dis
}
public struct SimpleJob : IJob
{
//Job이라는 쓰레드를 만들기 위해 구조체를 사용한다. 또한, IJob 인터페이스를 구현하게 한다.
public int a;
public int b;
public int c;
public NativeReference<int> result;
public void Execute() //IJob 인터페이스의 Execute()을 구현해야 함.
{
// c = a + b;
result.Value = a + b;
}
}
}
using Unity.Collections;
using Unity.Entities.UniversalDelegates;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
public class JobTest2 : MonoBehaviour
{
[SerializeField]
private bool usejob;
// Update is called once per frame
public void Update()
{
float startTime = Time.realtimeSinceStartup;
if(usejob)
{
NativeList<JobHandle> jobHandleList = new NativeList<JobHandle>();
for (int i = 0; i < 10; i++)
{
JobHandle handle = ToughTaskJob();
jobHandleList.Add(handle);
}
JobHandle.CompleteAll(jobHandleList.AsArray());
jobHandleList.Dispose();
}
else
{
for(int i = 0; i< 10; i++)
{
ToughTask();
}
}
Debug.Log((Time.realtimeSinceStartup - startTime) * 1000f + "ms");
}
void ToughTask()
{
float value = 0f;
for(int i = 0; i<10000; i++)
{
value = math.exp10(math.sqrt(value));
}
}
JobHandle ToughTaskJob()
{
ToughJob job = new ToughJob();
return job.Schedule();
}
}
public struct ToughJob : IJob
{
public void Execute()
{
float value = 0f;
for (int i = 0; i < 1000; i++)
{
value = math.exp10(math.sqrt(value));
}
}
}
using System.Collections.Generic;
using Unity.Collections;
using Unity.Entities.UniversalDelegates;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
public class JobTest2 : MonoBehaviour
{
[SerializeField]
private bool usejob;
[SerializeField]
GameObject[] zombiePrefab;
List<Zombie> zombieList;
public class Zombie
{
public Transform transform;
public float moveY;
public float rotateY;
}
private void Start()
{
zombieList = new List<Zombie>();
for(int i = 0; i<1000; i++)
{
GameObject go = Instantiate(zombiePrefab[UnityEngine.Random.Range(0, zombiePrefab.Length)], //게임오브젝트 배열에 저장된 좀비 1 , 좀비 2 둘중 하나를 랜덤으로 생성
new Vector3(UnityEngine.Random.Range(-8f,8f), //위치
UnityEngine.Random.Range(-4f,4f),
UnityEngine.Random.Range(-8f,8f)),
Quaternion.identity
); //유니티 엔진 랜덤을 사용
zombieList.Add(new Zombie
{
transform = go.transform,
moveY = UnityEngine.Random.Range(1f, 2f),
rotateY = UnityEngine.Random.Range(180f,360f)
}); //좀비 리스트에 위 데이터를 추가.
}
}
private void Update()
{
float startTime = Time.realtimeSinceStartup;
if(usejob)
{
//NativeList<JobHandle> jobHandleList = new NativeList<JobHandle>();
//for(int i = 0; i< 10; i++)
//{
// JobHandle handle = ToughTaskJob();
// jobHandleList.Add(handle);
//}
//JobHandle.CompleteAll(jobHandleList.AsArray());
//jobHandleList.Dispose();
}
else
{
foreach(Zombie zombie in zombieList)
{
zombie.transform.position += new Vector3(0, zombie.moveY * Time.deltaTime, 0); //y축으로 움직임.
if(zombie.transform.position.y > 4f)
{
zombie.moveY = -math.abs(zombie.moveY);
}
if (zombie.transform.position.y > -4f)
{
zombie.moveY = math.abs(zombie.moveY);
}
zombie.transform.Rotate(new Vector3(0, zombie.rotateY * Time.deltaTime, 0));
ToughTask();
}
}
Debug.Log((Time.realtimeSinceStartup - startTime) * 1000f + "ms");
}
void ToughTask()
{
float value = 0f;
for(int i = 0; i<1000; i++)
{
value = math.exp10(math.sqrt(value));
}
}
JobHandle ToughTaskJob()
{
ToughJob job = new ToughJob();
return job.Schedule();
}
}
public struct ToughJob : IJob
{
public void Execute()
{
float value = 0f;
for (int i = 0; i < 1000; i++)
{
value = math.exp10(math.sqrt(value));
}
}
}
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities.UniversalDelegates;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
using static JobTest2;
public class JobTest2 : MonoBehaviour
{
[SerializeField]
private bool usejob;
[SerializeField]
GameObject[] zombiePrefab;
List<Zombie> zombieList;
public class Zombie
{
public Transform transform;
public float moveY;
public float rotateY;
}
private void Start()
{
zombieList = new List<Zombie>();
for(int i = 0; i<1000; i++)
{
GameObject go = Instantiate(zombiePrefab[UnityEngine.Random.Range(0, zombiePrefab.Length)], //게임오브젝트 배열에 저장된 좀비 1 , 좀비 2 둘중 하나를 랜덤으로 생성
new Vector3(UnityEngine.Random.Range(-8f,8f), //위치
UnityEngine.Random.Range(-4f,4f),
UnityEngine.Random.Range(-8f,8f)),
Quaternion.identity
); //유니티 엔진 랜덤을 사용
zombieList.Add(new Zombie
{
transform = go.transform,
moveY = UnityEngine.Random.Range(1f, 2f),
rotateY = UnityEngine.Random.Range(180f,360f)
}); //좀비 리스트에 위 데이터를 추가.
}
}
private void Update()
{
float startTime = Time.realtimeSinceStartup;
if(usejob)
{
NativeArray<float3> positionArray = new NativeArray<float3>(zombieList.Count, Allocator.TempJob);
NativeArray<float> moveYArray = new NativeArray<float>(zombieList.Count, Allocator.TempJob);
NativeArray<float3> rotationArray = new NativeArray<float3>(zombieList.Count, Allocator.TempJob);
NativeArray<float> rotateYArray = new NativeArray<float>(zombieList.Count, Allocator.TempJob);
for (int i = 0; i<zombieList.Count; i++)
{
positionArray[i] = zombieList[i].transform.position;
moveYArray[i] = zombieList[i].moveY;
}
ZombieMovingJobFor zombieMovingJobFor = new ZombieMovingJobFor
{
positionArray = positionArray,
moveYArray = moveYArray,
deltaTime = Time.deltaTime
};
JobHandle zombieMovingForHandle = zombieMovingJobFor.ScheduleParallel(zombieList.Count, 50,default);
zombieMovingForHandle.Complete();
for(int i = 0; i< zombieList.Count; i++)
{
zombieList[i].transform.position = positionArray[i];
zombieList[i].moveY = moveYArray[i];
zombieList[i].transform.eulerAngles = rotationArray[i];
}
positionArray.Dispose();
moveYArray.Dispose();
rotationArray.Dispose();
}
else
{
foreach(Zombie zombie in zombieList)
{
zombie.transform.position += new Vector3(0, zombie.moveY * Time.deltaTime, 0); //y축으로 움직임.
if(zombie.transform.position.y > 4f)
{
zombie.moveY = -math.abs(zombie.moveY);
}
if (zombie.transform.position.y > -4f)
{
zombie.moveY = math.abs(zombie.moveY);
}
zombie.transform.Rotate(new Vector3(0, zombie.rotateY * Time.deltaTime, 0));
ToughTask();
}
}
Debug.Log((Time.realtimeSinceStartup - startTime) * 1000f + "ms");
}
void ToughTask()
{
float value = 0f;
for(int i = 0; i<1000; i++)
{
value = math.exp10(math.sqrt(value));
}
}
JobHandle ToughTaskJob()
{
ToughJob job = new ToughJob();
return job.Schedule();
}
}
[BurstCompile]
public struct ToughJob : IJob
{
public void Execute()
{
float value = 0f;
for (int i = 0; i < 1000; i++)
{
value = math.exp10(math.sqrt(value));
}
}
}
[BurstCompile]
public struct ZombieMovingJobFor : IJobFor // IJob과 다르게 Execute 메서드에 인덱스 매개변수가 필요함.
{
//인덱스를 통해 인덱스에 맞는 연산을 해라!!!!
public NativeArray<float3> positionArray;
public NativeArray<float> moveYArray;
public NativeArray<float3> rotationArray;
public NativeArray<float> rotateYArray;
public float deltaTime;
public void Execute(int index)
{
positionArray[index] += new float3(0, moveYArray[index] * deltaTime, 0);
if (positionArray[index].y > 4f)
{
moveYArray[index] = -math.abs(moveYArray[index]);
}
if (positionArray[index].y > -4f)
{
moveYArray[index] = math.abs(moveYArray[index]);
}
rotationArray[index] += new float3(0, rotateYArray[index] * deltaTime, 0);
float value = 0f;
for (int i = 0; i < 1000; i++)
{
value = math.exp10(math.sqrt(value));
}
}
}




using UnityEngine;
public class FindNearast : MonoBehaviour
{
void Update()
{
foreach(Transform seekerTransform in Spawner.seekerTransforms)
{
Vector3 seekerPos = seekerTransform.position;
Vector3 nearestTarget = default;
float nearestDist = float.MaxValue;
foreach(Transform targetTransform in Spawner.targetTransforms)
{
Vector3 offset = targetTransform.position - seekerPos;
float dist = offset.sqrMagnitude;
if(dist < nearestDist)
{
nearestDist = dist;
nearestTarget = targetTransform.position;
}
}
Debug.DrawLine(seekerPos, nearestTarget);
}
}
//위 코드는 O(n2)이 소요되는 시간이 된다.
//매 프레임마다 O(n2)을 계산하면 성능면에서 안 좋다. -> 최적화 필요함
}