... | ... | @@ -171,7 +171,185 @@ class Program |
|
|
}
|
|
|
```
|
|
|
|
|
|
|
|
|
# 2. "시작하세요. C# 프로그래밍" 책에서 P651 ~ P667 스터디하시고 정리해 주세요.
|
|
|
### 비동기 호출
|
|
|
비동기 호출은 별도의 thread를 만들고 callback을 전달하고 thread의 종료 시점을 대기해서 다음 작업을 진행해야 하는 등의 복잡한 흐름으로 코드를 작성하기가 어려운 부분이 있었다.
|
|
|
|
|
|
그래서 C# 5.0에서는 비동기 호출을 동기 방식처럼 호출하는 코드를 작성할 수 있도록 `async`/`await` 예약어 제공한다.
|
|
|
|
|
|
`async` : 메서드 선언시 사용되는 예약어로 해당 메서드를 비동기로 실행하라는 의미다.
|
|
|
|
|
|
`await` : 비동기 메서드에서만 인식되며 ~Async류의 비동기 호출이 끝날때 까지 대기한다.
|
|
|
|
|
|
```csharp
|
|
|
class Program
|
|
|
{
|
|
|
static void Main(string[] args)
|
|
|
{
|
|
|
AwaitRead();
|
|
|
Console.ReadLine();
|
|
|
}
|
|
|
|
|
|
private static async void AwaitRead()
|
|
|
{
|
|
|
using (FileStream fs = new FileStream(@"C:\windows\system32\drivers\etc\HOSTS", FileMode.Open))
|
|
|
{
|
|
|
byte[] buf = new byte[fs.Length];
|
|
|
await fs.ReadAsync(buf, 0, buf.Length);
|
|
|
|
|
|
// ReadAsync 메서드가 완료된 후에 아래 코드가 호출된다.
|
|
|
string txt = Encoding.UTF8.GetString(buf);
|
|
|
Console.WriteLine(txt);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### 닷넷 4.5 BCL에 추가된 Async 메서드
|
|
|
`async`/`await`의 도입으로 BCL 라이브러리에 제공되던 복잡한 비동기 처리에 비동기 호출이 가능한 메서드들이 추가됐다.
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
class Program
|
|
|
{
|
|
|
static void Main(string[] args)
|
|
|
{
|
|
|
// WebClinet 타입
|
|
|
AwaitDownloadString();
|
|
|
Console.ReadLine();
|
|
|
|
|
|
// TCP 통신
|
|
|
TcpListener listener = new TcpListener(IPAddress.Any, 11200);
|
|
|
listener.Start();
|
|
|
|
|
|
while (true)
|
|
|
{
|
|
|
var client = listener.AcceptTcpClient();
|
|
|
ProcessTcpClient(client);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private static async void AwaitDownloadString()
|
|
|
{
|
|
|
WebClient wc = new WebClient();
|
|
|
wc.Encoding = Encoding.UTF8;
|
|
|
string text = await wc.DownloadStringTaskAsync("http://www.naver.com");
|
|
|
Console.WriteLine(text);
|
|
|
}
|
|
|
|
|
|
private static async void ProcessTcpClient(TcpClient client)
|
|
|
{
|
|
|
NetworkStream ns = client.GetStream();
|
|
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
int received = await ns.ReadAsync(buffer, 0, buffer.Length);
|
|
|
|
|
|
string txt = Encoding.UTF8.GetString(buffer, 0, received);
|
|
|
|
|
|
byte[] sendBuffer = Encoding.UTF8.GetBytes("Hello:" + txt);
|
|
|
await ns.WriteAsync(sendBuffer, 0, sendBuffer.Length);
|
|
|
ns.Close();
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
이 밖에도 닷넷 4.5에는 I/O를 담당하는 Stream 기반 클래스를 비롯해 몇몇 타입에 기존 Begin/End 델리게이트로 구현된 비동기 메서드에 대응하는 Async 메서드가 추가됐다.
|
|
|
|
|
|
|
|
|
#### Task, Task<TResult> 타입
|
|
|
Task는 닷넷 4.0에 추가된 병렬 처리 라이브러리(TPL:Task Parallel Library)에 속한 타입으로 비동기 작업을 위해 스레드 처럼 사용 가능하다.
|
|
|
|
|
|
```csharp
|
|
|
class Program
|
|
|
{
|
|
|
static void Main(string[] args)
|
|
|
{
|
|
|
// 기존 ThreadPool QueueUserWorkItem 사용
|
|
|
ThreadPool.QueueUserWorkItem((obj) => { Console.WriteLine("process workitem"); }, null);
|
|
|
|
|
|
// 닷넷 4.0 Task 타입 사용
|
|
|
Task task1 = new Task(() => { Console.WriteLine("process taskitem"); });
|
|
|
task1.Start();
|
|
|
|
|
|
// Task 작업 완료 대기
|
|
|
Task TaskSleep = new Task(() => { Thread.Sleep(5000); });
|
|
|
TaskSleep.Start();
|
|
|
TaskSleep.Wait();
|
|
|
|
|
|
// 객체 생성없이 Task 실행
|
|
|
Task.Factory.StartNew(() => { Console.WriteLine("process taskitem"); });
|
|
|
|
|
|
// 값을 반환하는 Task 실행
|
|
|
Task<int> task = new Task<int>(() => { return new Random((int)DateTime.Now.Ticks).Next(); });
|
|
|
task.Start();
|
|
|
task.Wait();
|
|
|
Console.WriteLine("무작위 숫자 값: " + task.Result);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### Async 메서드가 아닌 경우의 비동기 처리
|
|
|
Task는 Async 처리가 적용되지 않은 메서드와 사용자 정의 메서드를 비동기 호출하는 방법을 제공한다.
|
|
|
|
|
|
```csharp
|
|
|
class Program
|
|
|
{
|
|
|
static void Main(string[] args)
|
|
|
{
|
|
|
AwaitFileRead(@"C:\windows\system32\drivers\etc\HOSTS");
|
|
|
Console.ReadLine();
|
|
|
}
|
|
|
|
|
|
private static async void AwaitFileRead(string filePath)
|
|
|
{
|
|
|
string fileText = await ReadAllTextAsync(filePath);
|
|
|
Console.WriteLine(fileText);
|
|
|
}
|
|
|
|
|
|
static Task<string> ReadAllTextAsync(string filePath)
|
|
|
{
|
|
|
return Task.Factory.StartNew(() => { return File.ReadAllText(filePath); });
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### 비동기 호출의 병렬 처리
|
|
|
`await`와 `Task`의 조합으로 비동기 호출의 병렬 작업이 가능하다.
|
|
|
|
|
|
아래 예제는 1에서 1000사이의 난수를 만들고 평균을 계산하는 난수 생성기를 인스턴스화하여 병렬로 비동기 호출을 하는 예제이다.
|
|
|
```csharp
|
|
|
public class Example
|
|
|
{
|
|
|
public static void Main()
|
|
|
{
|
|
|
var tasks = new List<Task<long>>(); // 난수 생성기에서 반환한 값을 저장할 리스트 생성
|
|
|
for (int ctr = 1; ctr <= 10; ctr++)
|
|
|
{
|
|
|
int delayInterval = 18 * ctr;
|
|
|
tasks.Add(Task.Run(async () => { // async 이용한 익명 함수를 생성하여 task를 수행
|
|
|
long total = 0;
|
|
|
await Task.Delay(delayInterval); // 랜덤 시드값을 위해 delay
|
|
|
var rnd = new Random();
|
|
|
for (int n = 1; n <= 1000; n++)
|
|
|
total += rnd.Next(0, 1000); // 난수 생성
|
|
|
|
|
|
return total;
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
var continuation = Task.WhenAll(tasks); // Non-Blocking Call
|
|
|
|
|
|
long grandTotal = 0;
|
|
|
foreach (var result in continuation.Result)
|
|
|
{
|
|
|
grandTotal += result;
|
|
|
Console.WriteLine("Mean: {0:N2}, n = 1,000", result / 1000.0);
|
|
|
}
|
|
|
|
|
|
Console.WriteLine("\nMean of Means: {0:N2}, n = 10,000", grandTotal / 10000);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
```
|
|
|
# 3. 예제 분석 1
|
|
|
# 4. 예제 분석 2 |