|
|
교재 "시작하세요 C# 프로그래밍 6" 기준으로
|
|
|
# 1. "시작하세요. C# 프로그래밍" 책에서 P416 ~ P449 스터디하시고 정리해 주세요.
|
|
|
|
|
|
10장 2절 비동기 호출을 스터디하셔서 정리해주세요.
|
|
|
### 스레딩
|
|
|
***스레드(thread)***는 명령어를 실행하기 위한 스케줄링 단위이며, 프로세스 내부에서 생성할 수 있다.
|
|
|
|
|
|
11장 C# 6.0 을 스터디하셔서 정리해주세요. |
|
|
윈도우 운영체제는 프로세스를 생성할 때 기본적으로 한 개의 스레드를 함께 생성하며, 이를 주 스레드(main thread, primary thread)라고 한다.
|
|
|
|
|
|
스레드는 CPU의 명령어 실행과 관련된 정보를 보관하고 있는데, 이를 스레드 문맥(thread context)이라 한다.
|
|
|
|
|
|
운영체제 스케줄러의 스케줄링 알고리즘에 따라 스레드의 문맥 교환(context switching)이 발생한다.
|
|
|
|
|
|
단일 스레딩의 경우 스레드가 작업 중에는 다른 작업을 할 수 없다. 멀티 스레딩은 스레드가 동시에 동작하기 때문에 다중 작업이 가능하다.
|
|
|
|
|
|
|
|
|
### System.Threading.Thread
|
|
|
|
|
|
프로그램이 실행되면 주 스레드가 하나 실행되고 새로 생성된 스레드를 포함한 모든 스레드가 종료되어야 프로그램이 종료된다.
|
|
|
|
|
|
- 전경 스레드(foreground thread) : 프로그램 실행 종료에 영향을 미치는 스레드
|
|
|
- 배경 스레드(background thread) : 프로그램 실행 종료에 영향을 미치지 않는 스레드
|
|
|
|
|
|
```csharp
|
|
|
// 스레드 생성, 시작, 대기
|
|
|
class Program
|
|
|
{
|
|
|
static void Main(string[] args)
|
|
|
{
|
|
|
Thread t = new Thread(threadFunc); // 스레드 생성
|
|
|
t.IsBackground = true; // 배경 스레드로 변경
|
|
|
t.Start(); // t 스레드 시작
|
|
|
|
|
|
t.Join(); // t 스레드가 종료할 때 까지 현재 스레드를 무한 대기
|
|
|
Console.WriteLine("주 스레드 종료");
|
|
|
}
|
|
|
|
|
|
static void threadFunc()
|
|
|
{
|
|
|
Console.WriteLine("60초 후에 프로그램 종료");
|
|
|
Thread.Sleep(1000 * 60); // 60초 동안 실행 중지
|
|
|
Console.WriteLine("스레드 종료!");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 60초 후에 프로그램 종료
|
|
|
// 스레드 종료!
|
|
|
// 주 스레드 종료!
|
|
|
```
|
|
|
|
|
|
### System.Threading.Monitor
|
|
|
|
|
|
멀티 스레딩 환경에서 여러 스레드가 동시에 실행되는 경우 여러 스레드에서 공유 리소스(shared resource)에 접근이 자유로우므로
|
|
|
예측할 수 없는 동작이 발생할 수 있다. 따라서 공유 리소스에 대한 스레드 동기화(synchronization) 처리가 필요하다.
|
|
|
BCL에서 스레드 동기화를 위한 Monitor 클래스를 제공한다.
|
|
|
|
|
|
메서드도 여러 스레드에서 동시 접근이 가능하므로 동기화가 필요하다.
|
|
|
BCL의 모든 정적 멤버는 다중 스레드 접근에 안전하지만, 인스턴스 멤버는 안전하지 않기 때문에, 동기화가 필요할 때는 직접 동기화 처리를 해야 한다.
|
|
|
|
|
|
```csharp
|
|
|
class Program
|
|
|
{
|
|
|
int number = 0;
|
|
|
static void Main(string[] args)
|
|
|
{
|
|
|
Thread t = new Thread(threadFunc);
|
|
|
t.Start();
|
|
|
}
|
|
|
static void threadFunc(object inst)
|
|
|
{
|
|
|
Program pg = inst as Program;
|
|
|
|
|
|
for (int i = 0; i < 100000; i++)
|
|
|
{
|
|
|
// Monitor 클래스를 이용한 동기화
|
|
|
Monitor.Enter(pg);
|
|
|
|
|
|
try
|
|
|
{
|
|
|
pg.number = pg.number + 1;
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
Monitor.Exit(pg);
|
|
|
}
|
|
|
|
|
|
// lock 예약어를 이용한 동기화 간편 표기
|
|
|
lock (pg)
|
|
|
{
|
|
|
pg.number = pg.number + 1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### System.Threading.Interlocked
|
|
|
다중 스레드의 공유자원을 사용하는 몇몇 패턴에 대해서 명시적인 동기화 작업을 단순하게 만드는 정적 메서드를 제공한다.
|
|
|
|
|
|
`Interlocked` 타입의 정적 메서드로 제공되는 연산의 단위를 ***원자적 연산(atomic operation)*** 이라고 하는데, 더 이상 쪼갤 수 없는 연산 단위로 이해할 수 있다.
|
|
|
|
|
|
#### Interlocked 정적 메서드
|
|
|
- CompareExchange : 두 대상을 비교하여 값이 같으면 지정된 값을 설정하고, 그렇지 않으면 연산을 수행하지 않는다.
|
|
|
|
|
|
- Decrement : 지정된 변수의 값을 감소시키고 저장한다.
|
|
|
|
|
|
- Exchange : 변수를 지정된 값으로 설정한다.
|
|
|
|
|
|
- Increment : 지정된 변수의 값을 증가시키고 저장한다.
|
|
|
|
|
|
|
|
|
|
|
|
### System.Threading.ThreadPool
|
|
|
임시적인 목적으로 스레드를 사용할 수 있는 스레드 풀.
|
|
|
스레드를 자주 생성해서 사용하는 경우 Thread 객체를 생성하기보다는 TheadPool로 부터 재사용했을 때 더 나은 성능을 보인다.
|
|
|
|
|
|
```csharp
|
|
|
TheadPool.QueueUserWorkItem(threadFunc, data);
|
|
|
```
|
|
|
|
|
|
### System.Threading.EventWaitHandle
|
|
|
Monitor 타입처럼 스레드 동기화 수단의 하나다.
|
|
|
|
|
|
이벤트 객체는 Signal과 Non-Signal 상태를 갖는다.
|
|
|
|
|
|
이벤트 객체의 상태는 `Set`, `Reset` 메서드로 전환한다.
|
|
|
|
|
|
WaitOne을 통해 이벤트 객체의 상태를 판단하여 제어를 반환하거나 대기한다.
|
|
|
|
|
|
이벤트는 수동 리셋(manual reset)과 자동 리셋(auto reset)으로 나뉜다.
|
|
|
- 수동 리셋(manual reset) : `Set` 메서드를 호출하여 Signal 상태가 된 후에 계속 Signal 상태로 머무는 이벤트. Reset 메서드를 호출하여 Non-Signal 상태로 돌릴 수 있다.
|
|
|
- 자동 리셋(auto reset) : `Set` 메서드를 호출하여 Signal 상태가 된 후에 바로 Non-Signal 상태로 자동으로 변경되는 이벤트.
|
|
|
|
|
|
|
|
|
|
|
|
### 비동기 호출(Asynchronous Call)
|
|
|
비동기 호출은 동기 호출(Synchronous Call)에서 발생하는 스레드 점유 문제를 해결하기 위해 제공된다.
|
|
|
C#에서는 스트림 객체의 비동기 호출을 위해 Read/Write 메서드에 대해 각각 `BeginRead`/`EndRead`, `BeginWrite`/`EndWrite` 메서드를 쌍으로 제공한다.
|
|
|
|
|
|
### System.Delegate의 비동기 호출
|
|
|
C#에서는 입출력 장치뿐만 아니라 일반 메서드에 대해서도 델리게이트를 통한 비동기 호출을 제공한다.
|
|
|
델리게이트의 비동기 호출을 위한 메서드 `BeginInvoke`/`EndInvoke`를 사용하면 ThreadPool을 이용한 비동기 호출을 사용할 수 있다.
|
|
|
|
|
|
```csharp
|
|
|
public class Calc
|
|
|
{
|
|
|
public static long Cumsum(int start, int end)
|
|
|
{
|
|
|
long sum = 0;
|
|
|
for (int i = start; i <= end; i++)
|
|
|
{
|
|
|
sum += i;
|
|
|
}
|
|
|
|
|
|
return sum;
|
|
|
}
|
|
|
}
|
|
|
class Program
|
|
|
{
|
|
|
public delegate long CalcMethod(int start, int end);
|
|
|
|
|
|
static void Main(string[] args)
|
|
|
{
|
|
|
CalcMethod calc = new CalcMethod(Calc.Cumsum);
|
|
|
|
|
|
IAsyncResult ar = calc.BeginInvoke(1, 100, null, null);
|
|
|
ar.AsyncWaitHandle.WaitOne();
|
|
|
|
|
|
long result = calc.EndInvoke(ar);
|
|
|
|
|
|
Console.WriteLine(result);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
|
|
|
2. "시작하세요. C# 프로그래밍" 책에서 P651 ~ P667 스터디하시고 정리해 주세요.
|
|
|
3. 예제 분석 1
|
|
|
4. 예제 분석 2 |