클로저
gc내용의 일환
외부변수나 필드와 같은 '환경'을 저장하고 있는 함수
람다식에서 내용을 전달할때 외부 변수/필드 사용시 Closure처리가 됨
=> 위 예제에서 for문안에 람다식으로 0~9까지 index를 출력하는 함수를 action에 델리게이트로 넣었는데
호출해줄때 해당 index값은 결국 10이되어 10만 출력하는 상황이 벌어짐
각 for문마다 초기화되는 local변수를 사용해주어 이 문제를 해결
실수할 여지가 있어서 조심해야할듯
COM
MS의 소프트웨어 컴포넌트 규격
p509 .NET은 RCW를 통해 COM객체를 호출해 사용 (RCW는 COM에 대한 프록시 역할)
액셀이나 액세스 등의 자료들을 읽어올 수 있음.
COM사용 예제
https://nomad-programmer.tistory.com/205
[Programming/C#] COM (Component Object Model)
COM 이란? COM은 Component Object Model의 약자로, 마이크로소프트의 소프트웨어 컴포넌트 규격을 말한다. OLE, ActiveX, COM+와 같은 파생 규격들이 모두 COM을 바탕으로 만들어졌다. 마이크로소프트에서 출
nomad-programmer.tistory.com
프로세스, 스레드, 태스크, 동기화/비동기화
프로세스 인스턴스 하나는 프로그램 실행의 단위
해당 프로그램안에서 일어나는 일을 프로세스 안의 스레드들이 동시에 처리하기도 함
여러 프로그램이 동시에 돌아가기도 하기 때문에 프로세스 인스턴스도 여럿 할당되어 실행
context switching
스레드의 상태
Unstarted : 생성후 Start() 호출 이전의 상태
Running : Start() 호출후 실행중
Suspended : Suspend() 메소드를 통해 중지 Start를 통해 다시 Running이 될 수 있음
WaitSleepJoin : 스레드가 블록된 상태 Enter, Sleep, Join을 호출하면 이상태가 됨
Aborted : Abort가 호출되에 완전히 중지된 상태. 이후 Stopped상태가 되어 완전히 중단
Stopped : 위의 상태에서 넘어오거나, 실행 중인 메소드가 종료되면 여기로
Background : 스레드가 백그라운드로 동작중인 상태.
foreground 쓰레드가 하나라도 살아있다면 프로세스는 죽지않지만 background스레드는 아무리 많아도 프로세스가 죽을 수 있다.
Thread.isBackground = true로 설정가능
Interrupt() 로 멈춤 : Abort는 무조건적으로 중단시키지만,
Interrupt는 지켜본 뒤에 중단, 절대로 중단되지 않는 작업은 중단시키지 않고 보장됨
ThreadInterruptedException예외를 발생
스레드 동기화
lock : critical section을 생성
Monitor : 저수준 동기화 지원
Wait() Pulse()의 존재 때문에 lock대신 Monitor를 사용 하게 됨 (스레드 객체에 대한 저수준 관리)
둘다 lock안에서만 호출해야함 (하지 않으면 SynchronizationLockException 발생)
예제 1. lock 키워드로 동기화 하기
static void DoSomething()
{
for(int i = 0; i < 5; i++)
{
Console.WriteLine("DoSomething : {0}", i);
//Thread.Sleep(10); //Sleep 예시
}
}
public static int count = 0;
private static object thisLock = new object();
//lock 설정되어 비동기 환경에서 동시에 실행되지 않는 메소드
//Critical Section
static void Increase()
{
lock(thisLock)
{
count++;
}
}
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(DoSomething));
Thread t2 = new Thread(new ThreadStart(DoSomething));
Thread t3 = new Thread(new ThreadStart(DoSomething));
//Start호출 메인 스레드에서 분기됨
t1.Start();
t2.Start();
t3.Start();
//Join 위에서 초기화된 DoSomething함수를 비동기로 실행
t1.Join();
t2.Join();
t3.Join();
t1.Interrupt(); //중단
}
예제 2. Monitor로 동기화 하기
//Monitor의 기본 사용
//lock과 완전히 같은 기능이고, 실제로 lock은 Enter(), Exit() 메소드를 바탕으로 만들어 짐
public void IncreaseByMonitor()
{
int loopCount = LOOP_COUNT;
while (loopCount-- > 0)
{
Monitor.Enter(thisLock);
try
{
count++;
}
finally
{
Monitor.Exit(thisLock);
}
Thread.Sleep(1);
}
}
//Monitor Wait Pulse사용
//Wait() 사용하면 WaitSleepJoin상태가 됨
//lock해제 이후엔 Waiting queue 에 진입, pulse가 호출되면 ReadyQueue로 이동
//객체를 해제하지 않고, 계속해서 사용하려고 할 때
public void IncreaseByWaitPulse()
{
int loopCount = LOOP_COUNT;
while (loopCount-- > 0)
{
lock (thisLock)
{
while (count > 0 || lockedCount == true)
{
Monitor.Wait(thisLock);//lock된 경우 기다림
//Sleep은 Pulse()로 깨울수 없음, 입력시간이 경과되거나 인터럽트 예외가 발생해야 깨어남
}
lockedCount = true;
count++;
lockedCount = false;
Monitor.Pulse(thisLock);
}
}
}
예제 3. 비동기 실행결과를 알려주는 Task<TResult> 클래스
var myTask = Task<List<int>>.Run(
() =>
{
Thread.Sleep(1000);
List<int> list = new List<int>();
list.Add(3);
list.Add(4);
list.Add(5);
return list; //TResult형식으로 결과 반환
}
)
List<int> myList = new List<int>();
myList.Add(3);
myList.Add(4);
myList.Add(5);
myTask.Wait(); //비동기 작업이 끝나야 TResult가 반환되므로 꼭 호출하지 않아도 되지만 나쁜 습관이 될 수 있다.
myList.AddRange(myTask.Result.ToArray()); // IEnumerable<T> collection으로 받음
예제4. 병렬처리
병렬 처리와 비동기 처리의 차이
병렬처리 (하나의 일 여러 작업자) : 하나의 작업을 여러 작업자가 나누는 것
비동기 처리 (여러 일 하나의 작업자) : 작업자가 A작업 도중 결과를 기다리는 동안 B작업, C작업도 같이 수행하는 방식
//Parallel을 호출만 하면 Parallel클래스가 알아서 판단해 최적화 함
//소수 찾기 예제
Paralell.For(from, to, (long i) =>
{
if(IsPrime(i))
total.Add(i);
});
예제5. async한정자 await 연산자로 만드는 비동기 코드
async한정자 사용하면 해당 메소드는 비동기 메소드가 됨 (반환형으로 반드시 Task, Task<TResult>, void만 허용)
async메소드 안의 반환형이 void면 비동기로
Task, Task<TResult>이고 await연산자가 없으면 동기 함수 코드로 동작 (async로 선언한 의미가 없어진다)
이 경우 await를 만나는 순간 비동기 실행이 시작됨
=> 이 함수를 호출한 부분에서 동기 실행을 하고 있었으니 멈추고 있던걸
호출한 부분이후와 await이후를 비동기 실행하게 됨
using System;
using System.Threading.Tasks;
namespace CodeTest
{
class Program
{
async static private void MyMethodAsync(int count)
{
Console.WriteLine("C");
Console.WriteLine("D");
await Task.Run(async () =>
{
for(int i = 1; i <= count; i++)
{
Console.WriteLine("{0}/{1} ...", i, count);
await Task.Delay(100);
}
});
Console.WriteLine("G");
Console.WriteLine("H");
}
static void Main(string[] args)
{
Console.WriteLine("A");
Console.WriteLine("B");
MyMethodAsync(3);
Console.WriteLine("E");
Console.WriteLine("F");
Console.ReadLine(); //프로그램 종료 방지
}
}
}
//결과
A
B
C
D
E
F
1/3
2/3
3/3
G
H
이 예제로 비동기 처리된 부분에서의 딜레이가 도는동안 함수 밖의 E,F가 실행된걸 볼수있음.
사실 이 예제는 0.1초를 기다리면 성능상 EF가 먼저 출력될 것이라고 신뢰하고 있지만,
실제로 0.1초보다 오래걸리는 작업을 할 경우엔 순서가 보장되지 않는다.
Sleep의 비동기 버전
await Task.Delay(100);
일정 시간후 Sleep 시킴
Sleep은 스레드 전체를 블록하지만 Delay는 스레드를 블록하지 않음
UI에서 Sleep이 호출되면 Sleep 반환까지 응답을 못하지만 Delay의 경우 메소드 반환과 상관없이 응답하게됨
IO의 동기 비동기 버전
ReadAsync, WriteAsync
'개발 공부 유니티, C#' 카테고리의 다른 글
UVCS (UnityVersionControlSystem) (0) | 2024.06.21 |
---|---|
개발자 정책 (1) | 2024.04.28 |