... | @@ -129,7 +129,7 @@ static void threadFunc(object inst) |
... | @@ -129,7 +129,7 @@ static void threadFunc(object inst) |
|
```
|
|
```
|
|
|
|
|
|
### **Interlocked**
|
|
### **Interlocked**
|
|
**Interlocked**는 다중 Thread의 공유자원에 대한 **Atomic Operation**를 제공한다. 특히 64bit 변수의 연산 과정에서 동기화 문제가 생길 수 있는데 **Atomic Operation**을 통해 하나의 Thread에서 한 번에 처리할 수 있다. 이 경우에 **Monitor**을 사용하여 Thread를 동기화할 수 있지만, **Interlocked**에서 제공하는 연산 기능으로 간단하게 구현할 수 있다.
|
|
**Interlocked**는 다중 Thread의 공유자원에 대한 **Atomic Operation**를 제공한다. 특히 64bit 변수의 연산 과정에서 동기화 문제가 생길 수 있는데 **Atomic Operation**을 하면 하나의 Thread에서 한 번에 처리할 수 있다. 이 경우에 **Monitor**을 사용하여 Thread를 동기화할 수 있지만, **Interlocked**에서 제공하는 연산 기능으로 간단하게 구현할 수 있다.
|
|
|
|
|
|
```
|
|
```
|
|
static void threadFunc(object inst)
|
|
static void threadFunc(object inst)
|
... | @@ -144,7 +144,7 @@ static void threadFunc(object inst) |
... | @@ -144,7 +144,7 @@ static void threadFunc(object inst) |
|
```
|
|
```
|
|
|
|
|
|
### **ThreadPool**
|
|
### **ThreadPool**
|
|
**ThreadPool**은 필요할 때마다 Thread를 꺼내 쓰고 다시 반환할 수 있는 기능을 제공한다. **Thread**를 따로 생성할 필요 없이 `QueueUserWorkItem()`에게 Thread가 자동으로 생성되고 지정한 Method가 할당되어 실행한다.
|
|
**ThreadPool**은 필요할 때마다 Thread를 꺼내 쓰고 다시 반환할 수 있는 기능을 제공한다. **Thread**를 따로 생성할 필요 없이 `QueueUserWorkItem()`를 사용하면 Thread가 자동으로 생성되고 지정한 Method가 할당되어 실행한다.
|
|
|
|
|
|
```
|
|
```
|
|
static void Main()
|
|
static void Main()
|
... | @@ -203,10 +203,10 @@ Main Thread End |
... | @@ -203,10 +203,10 @@ Main Thread End |
|
|
|
|
|
## **Acync**
|
|
## **Acync**
|
|
### **Async Method**
|
|
### **Async Method**
|
|
`acync`와 `await` 키워드를 사용하여 간편하게 **비동기 호출**을 할 수 있다. `async`는 내부에 `await`를 사용했으므로 해당 Method를 **Async Method**로 정의해주는 역할을 하고, `await`는 대기 작업이 완료될 때까지 Method의 실행을 일시 중단하여 비동기를 적용한다.
|
|
`acync`와 `await` 키워드를 사용하여 간편하게 **비동기 호출**을 할 수 있다. `async`는 내부에 `await`를 사용했으므로 해당 Method를 **Async Method**로 정의해주는 역할을 하고, `await`는 대기가 완료될 때까지 Method의 실행을 일시 중단하여 비동기를 적용한다.
|
|
|
|
|
|
```
|
|
```
|
|
async void AwaitDownloadString()
|
|
public async void AwaitDownloadString()
|
|
{
|
|
{
|
|
WebClient webClient = new WebClient();
|
|
WebClient webClient = new WebClient();
|
|
|
|
|
... | @@ -217,7 +217,7 @@ async void AwaitDownloadString() |
... | @@ -217,7 +217,7 @@ async void AwaitDownloadString() |
|
```
|
|
```
|
|
|
|
|
|
### **Task**
|
|
### **Task**
|
|
**Task**는 Thread Pool에서 Thread를 가져와 비동기 작업을 실행한다. **Task** 객체를 만들기 위해서 생성자에 **Method Delegate**를 지정한다.
|
|
**Task**는 Thread Pool을 사용하여 비동기 작업을 구현할 수 있도록 해준다.
|
|
|
|
|
|
#### **반환 값이 없는 Task**
|
|
#### **반환 값이 없는 Task**
|
|
```
|
|
```
|
... | @@ -225,7 +225,7 @@ Task task = new Tack(() => { Thread.Sleep(3000); }); |
... | @@ -225,7 +225,7 @@ Task task = new Tack(() => { Thread.Sleep(3000); }); |
|
task.Start();
|
|
task.Start();
|
|
```
|
|
```
|
|
|
|
|
|
* **Task**를 생성할 필요 없이 **Action Delegate**를 전달하고 바로 작업을 시작할 수 있다.
|
|
* `Task`를 생성할 필요 없이 **Action Delegate**를 전달하고 바로 작업을 시작할 수 있다.
|
|
|
|
|
|
```
|
|
```
|
|
Task task = Task.Factory.StartNew(() => { Thread.Sleep(3000); });
|
|
Task task = Task.Factory.StartNew(() => { Thread.Sleep(3000); });
|
... | @@ -237,7 +237,6 @@ Task task = Task.Factory.StartNew(() => { Thread.Sleep(3000); }); |
... | @@ -237,7 +237,6 @@ Task task = Task.Factory.StartNew(() => { Thread.Sleep(3000); }); |
|
Task<int> task = new Task<int>(() =>
|
|
Task<int> task = new Task<int>(() =>
|
|
{
|
|
{
|
|
Thread.Sleep(3000);
|
|
Thread.Sleep(3000);
|
|
|
|
|
|
return 3;
|
|
return 3;
|
|
});
|
|
});
|
|
|
|
|
... | @@ -246,13 +245,12 @@ task.Start(); |
... | @@ -246,13 +245,12 @@ task.Start(); |
|
Console.WriteLine(task.Result); // 출력 결과 : 3
|
|
Console.WriteLine(task.Result); // 출력 결과 : 3
|
|
```
|
|
```
|
|
|
|
|
|
* 마찬가지로 **Task<TResult>**는 **Func Delegate**를 전달하고 바로 작업을 시작할 수 있다.
|
|
* `Task<TResult>`를 생성할 필요 없이 **Func Delegate**를 전달하고 바로 작업을 시작할 수 있다.
|
|
|
|
|
|
```
|
|
```
|
|
Task<int> task = Task.Factory.StartNew<int>(() =>
|
|
Task<int> task = Task.Factory.StartNew<int>(() =>
|
|
{
|
|
{
|
|
Thread.Sleep(3000);
|
|
Thread.Sleep(3000);
|
|
|
|
|
|
return 3;
|
|
return 3;
|
|
});
|
|
});
|
|
|
|
|
... | @@ -264,14 +262,14 @@ Console.WriteLine(task.Result); // 출력 결과 : 3 |
... | @@ -264,14 +262,14 @@ Console.WriteLine(task.Result); // 출력 결과 : 3 |
|
**Async Method**가 아닌 Method의 반환 형식을 **Task<TResult>**로 바꾸면 `await`를 이용해 **비동기 호출**을 적용할 수 있다.
|
|
**Async Method**가 아닌 Method의 반환 형식을 **Task<TResult>**로 바꾸면 `await`를 이용해 **비동기 호출**을 적용할 수 있다.
|
|
|
|
|
|
```
|
|
```
|
|
async void AwaitDownloadString()
|
|
public async void AwaitDownloadString()
|
|
{
|
|
{
|
|
string text = await MyDownloadString("http://www.novonetworks.com/");
|
|
string text = await MyDownloadString("http://www.novonetworks.com/");
|
|
|
|
|
|
Console.WriteLine(text);
|
|
Console.WriteLine(text);
|
|
}
|
|
}
|
|
|
|
|
|
Task<string> MyDownloadString(string uri)
|
|
public Task<string> MyDownloadString(string uri)
|
|
{
|
|
{
|
|
WebClient webClient = new WebClient();
|
|
WebClient webClient = new WebClient();
|
|
|
|
|
... | @@ -333,4 +331,116 @@ static Task<int> FiveSeconds() |
... | @@ -333,4 +331,116 @@ static Task<int> FiveSeconds() |
|
Main Thread End
|
|
Main Thread End
|
|
Total : 8
|
|
Total : 8
|
|
Result : 5
|
|
Result : 5
|
|
``` |
|
```
|
|
\ No newline at end of file |
|
|
|
|
|
## **예제 분석**
|
|
|
|
### **SendRecvCommandTimeoutAsync()**
|
|
|
|
```
|
|
|
|
public async Task<Tuple<bool, int>> SendRecvCommandTimeoutAsync(/* 생략 */) { /* 생략 */ }
|
|
|
|
```
|
|
|
|
* `acyns`를 사용했으므로 비동기 호출이 가능한 Method이다.
|
|
|
|
* `bool`과 `int` 형식의 값을 저장한 `Tuple` 객체를 반환한다.
|
|
|
|
|
|
|
|
```
|
|
|
|
Task<int> cmdTask = SendRecvCommandAsync(client, ipAddr, cmd, ctag, sessionID);
|
|
|
|
```
|
|
|
|
* ENC로 부터 PDU를 전송받아 **calla_id**로 변환된 값을 반환하는 Task인 **SendRecvCommandAsync()**를 **cmdTask**에 저장한다.
|
|
|
|
|
|
|
|
```
|
|
|
|
Task timeoutTask = Task.Delay(timeoutMillis);
|
|
|
|
```
|
|
|
|
* 현재 Method에서 매개변수로 받은 값인 **timeoutMillis**(기본 값:10000) 만큼 Delay하는 Task를 **timeoutTask**에 저장한다.
|
|
|
|
|
|
|
|
```
|
|
|
|
result = await Task.Factory.ContinueWhenAny<bool>(new Task[] { cmdTask, timeoutTask }, (completedTask) => { /* 생략 */ });
|
|
|
|
```
|
|
|
|
* **cmdTask**와 **timeoutTask**를 동시에 실행하여 먼저 끝난 Task를 Func Delegate의 매개변수인 **completedTask**에 넘긴 후 Task를 실행한다. `await`를 사용했으므로 이 모든 Task는 비동기로 처리되고, Task의 반환 값은 **result**(`bool`)에 저장한다.
|
|
|
|
* **Task.Factory.ContinueWhenAny()**와 관련된 내용과 예제를 설명 하단에 추가하였다.
|
|
|
|
|
|
|
|
```
|
|
|
|
if (completedTask == timeoutTask)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if
|
|
|
|
{
|
|
|
|
/* 생략 */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* **completedTask**와 **timeoutTask**가 같다는 것은 cmdTask을 작업하는 데 제한시간을 초과했다는 것이므로 `false`를 반환한다. 이외 경우도 시간 초과로 보고 `false`를 반환한다.
|
|
|
|
|
|
|
|
```
|
|
|
|
else if (completedTask == cmdTask)
|
|
|
|
{
|
|
|
|
calla_id = cmdTask.Result;
|
|
|
|
|
|
|
|
int isValid = Calla.valid(calla_id);
|
|
|
|
if (isValid != 1)
|
|
|
|
{
|
|
|
|
Calla.close(calla_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (isValid == 1);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* **completedTask**와 **cmdTask**가 같다는 것은 제한시간 내에 **cmdTask**에서 **calla_id**를 반환했다는 것이다.
|
|
|
|
* **Calla.valid()**에서 **calla_id**를 확인한다. Invalid(-1)하면 Close하고, Valid(1)하면 `true`를 반환한다.
|
|
|
|
|
|
|
|
```
|
|
|
|
return new Tuple<bool, int>(result, calla_id);
|
|
|
|
```
|
|
|
|
* **Task.Factory.ContinueWhenAny()**에서 반환된 **result**와 **cmdTask**에서 반환된 **calla_id** 변수를 `Tuple`에 저장 후 객체를 생성하여 최종적으로 반환한다.
|
|
|
|
|
|
|
|
#### **Task.Factory.ContinueWhenAny()**
|
|
|
|
예제를 살펴보면 첫 번째 매개변수의 Task 배열 중 먼저 끝난 Task가 두 번째 Func Delegate의 매개변수로 전달되는 것을 알 수 있다.
|
|
|
|
|
|
|
|
```
|
|
|
|
public async void asyncTest()
|
|
|
|
{
|
|
|
|
var task1 = Task.Factory.StartNew(() =>
|
|
|
|
{
|
|
|
|
Thread.Sleep(5000);
|
|
|
|
return 1;
|
|
|
|
});
|
|
|
|
|
|
|
|
var task2 = Task.Factory.StartNew(() =>
|
|
|
|
{
|
|
|
|
Thread.Sleep(2000);
|
|
|
|
return 2;
|
|
|
|
});
|
|
|
|
|
|
|
|
var task3 = Task.Factory.StartNew(() =>
|
|
|
|
{
|
|
|
|
Thread.Sleep(3000);
|
|
|
|
return 3;
|
|
|
|
});
|
|
|
|
|
|
|
|
int result = await Task.Factory.ContinueWhenAny<int>(new[] { task1, task2, task3 }, (completedTask) =>
|
|
|
|
{
|
|
|
|
int id = -1;
|
|
|
|
|
|
|
|
if (completedTask.Equals(task1))
|
|
|
|
{
|
|
|
|
id = task1.Result;
|
|
|
|
}
|
|
|
|
else if (completedTask.Equals(task2))
|
|
|
|
{
|
|
|
|
id = task2.Result;
|
|
|
|
}
|
|
|
|
else if (completedTask.Equals(task3))
|
|
|
|
{
|
|
|
|
id = task3.Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return id;
|
|
|
|
});
|
|
|
|
|
|
|
|
Console.WriteLine(result); // 실행 결과 : 2 (task2의 반환 값)
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### **SendRecvCommandAsync()** |