... | @@ -72,12 +72,12 @@ Main Thread End |
... | @@ -72,12 +72,12 @@ Main Thread End |
|
```
|
|
```
|
|
|
|
|
|
### **Monitor**
|
|
### **Monitor**
|
|
**Monitor**는 임의의 객체를 잠금으로써 특정 코드의 접근을 막는 기능을 가지고 있다. 여러 Thread가 하나의 공유자원을 동시에 사용하면 문제가 발생하는데 **Monitor**를 통해 하나의 Thread에서만 공유자원을 사용할 수 있도록 하여 Thread를 동기화할 수 있다.
|
|
**Monitor**는 임의의 객체를 잠금으로써 특정 코드의 접근을 막는 기능을 가지고 있다. 여러 Thread가 하나의 공유자원을 동시에 사용하면 문제가 발생하는데 **Monitor**를 통해 하나의 Thread에서만 사용할 수 있도록 하여 Thread를 동기화할 수 있다.
|
|
|
|
|
|
```
|
|
```
|
|
static void Main()
|
|
static void Main()
|
|
{
|
|
{
|
|
MyData data = new MyData(); // MyData 클래스 생략
|
|
MyData data = new MyData(); // MyData 클래스 (생략)
|
|
|
|
|
|
Thread thread1 = new Thread();
|
|
Thread thread1 = new Thread();
|
|
Thread thread2 = new Thread();
|
|
Thread thread2 = new Thread();
|
... | @@ -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 변수에서 이러한 문제가 발생할 수 있는데 **Interlocked**을 사용해 하나의 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()
|
... | @@ -341,16 +341,13 @@ public async Task<Tuple<bool, int>> SendRecvCommandTimeoutAsync(/* 매개변수 |
... | @@ -341,16 +341,13 @@ public async Task<Tuple<bool, int>> SendRecvCommandTimeoutAsync(/* 매개변수 |
|
* `acyns`를 사용했으므로 비동기 호출이 가능한 Method이다.
|
|
* `acyns`를 사용했으므로 비동기 호출이 가능한 Method이다.
|
|
* `bool`과 `int` 형식의 값을 저장한 `Tuple` 객체를 가진 Task를 반환한다.
|
|
* `bool`과 `int` 형식의 값을 저장한 `Tuple` 객체를 가진 Task를 반환한다.
|
|
|
|
|
|
```
|
|
|
|
Task<int> cmdTask = SendRecvCommandAsync(client, ipAddr, cmd, ctag, sessionID);
|
|
|
|
```
|
|
|
|
* ENC로 부터 PDU를 전송받아 calla_id로 변환된 값을 반환하는 Task인 SendRecvCommandAsync()를 cmdTask에 저장한다.
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
```
|
|
```
|
|
|
|
Task<int> cmdTask = SendRecvCommandAsync(client, ipAddr, cmd, ctag, sessionID);
|
|
Task timeoutTask = Task.Delay(timeoutMillis);
|
|
Task timeoutTask = Task.Delay(timeoutMillis);
|
|
```
|
|
```
|
|
|
|
* ENC로 부터 PDU를 전송받아 calla_id로 변환된 값을 반환하는 Task인 SendRecvCommandAsync()를 cmdTask에 저장한다.
|
|
* 현재 Method에서 매개변수로 받은 값인 timeoutMillis(기본 값:10000) 만큼 Delay 하는 Task를 timeoutTask에 저장한다.
|
|
* 현재 Method에서 매개변수로 받은 값인 timeoutMillis(기본 값:10000) 만큼 Delay 하는 Task를 timeoutTask에 저장한다.
|
|
|
|
|
|
---
|
|
---
|
... | @@ -362,6 +359,8 @@ result = await Task.Factory.ContinueWhenAny<bool>(new Task[] { cmdTask, timeoutT |
... | @@ -362,6 +359,8 @@ result = await Task.Factory.ContinueWhenAny<bool>(new Task[] { cmdTask, timeoutT |
|
* `await`를 사용했으므로 이 모든 Task는 비동기로 처리되고, Task의 반환 값은 result에 저장한다.
|
|
* `await`를 사용했으므로 이 모든 Task는 비동기로 처리되고, Task의 반환 값은 result에 저장한다.
|
|
* Task.Factory.ContinueWhenAny()와 관련된 내용과 예제를 설명 하단에 추가하였다.
|
|
* Task.Factory.ContinueWhenAny()와 관련된 내용과 예제를 설명 하단에 추가하였다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
```
|
|
```
|
|
if (completedTask == timeoutTask)
|
|
if (completedTask == timeoutTask)
|
|
{
|
|
{
|
... | @@ -547,14 +546,14 @@ string myCmd = "%%" + cmd; |
... | @@ -547,14 +546,14 @@ string myCmd = "%%" + cmd; |
|
int calla_id = -1;
|
|
int calla_id = -1;
|
|
```
|
|
```
|
|
* 전송할 명령어 앞에 기본적으로 "%%"를 추가한다. (ex. %%TEST)
|
|
* 전송할 명령어 앞에 기본적으로 "%%"를 추가한다. (ex. %%TEST)
|
|
* calla_id를 -1로 초기화해준다.
|
|
* calla_id를 –1로 초기화해준다. (Invalid = -1)
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
```
|
|
```
|
|
await client.ConnectAsync(ipAddr, EncClientManager.Port);
|
|
await client.ConnectAsync(ipAddr, EncClientManager.Port);
|
|
```
|
|
```
|
|
* IP(ipAddr)와 Port(EncClientManager.Port) TCP Sever(ENC)에 Client를 연결한다.
|
|
* IP(ipAddr)와 Port(EncClientManager.Port)에 해당하는 TCP Sever(ENC)에 Client를 연결한다.
|
|
* TCP Server와 연결하는 과정에서 대기시간이 길어지게 되면 병목이 발생하여 프로그램을 실행하는 동안에 문제가 발생할 수 있다. `await`를 사용해 비동기 처리를 함으로써 이러한 문제를 방지할 수 있다.
|
|
* TCP Server와 연결하는 과정에서 대기시간이 길어지게 되면 병목이 발생하여 프로그램을 실행하는 동안에 문제가 발생할 수 있다. `await`를 사용해 비동기 처리를 함으로써 이러한 문제를 방지할 수 있다.
|
|
|
|
|
|
---
|
|
---
|
... | @@ -636,8 +635,7 @@ Array.Copy(checksumBytes, 0, sendBuffer, (int)AppConst.PduHeader.checksun, check |
... | @@ -636,8 +635,7 @@ Array.Copy(checksumBytes, 0, sendBuffer, (int)AppConst.PduHeader.checksun, check |
|
```
|
|
```
|
|
* paddedCmdBytes에 대한 CRC 값을 checksum을 저장한다.
|
|
* paddedCmdBytes에 대한 CRC 값을 checksum을 저장한다.
|
|
* checksum을 byte 로 Convert 해서 checksumBytes에 저장한다.
|
|
* checksum을 byte 로 Convert 해서 checksumBytes에 저장한다.
|
|
* checksumBytesd를 뒤집어서 sendBuffer의 6~7번 요소에 복사한다.
|
|
* checksumBytesd를 뒤집어서 sendBuffer의 6~7번 요소에 복사한다. (Endian 변환)
|
|
* Endian 방식을 사용하기 때문에 Header의 데이터는 Reverse 해서 전송한다.
|
|
|
|
|
|
|
|

|
|

|
|
|
|
|
... | @@ -823,9 +821,11 @@ while (remainLength > 0) |
... | @@ -823,9 +821,11 @@ while (remainLength > 0) |
|
destIndex += readAmount;
|
|
destIndex += readAmount;
|
|
}
|
|
}
|
|
```
|
|
```
|
|
* 전송받은 Payload의 길이를 확인하기 위해 remainLength를 bodyLength의 값으로 저장하고, 전송받은 길이를 저장할 destIndex를 0으로 초기화 한다.
|
|
* 전송받은 후 남은 데이터의 길이를 확인할 변수인 remainLength에 Payload의 길이를 저장하고, 현재까지 전송받은 데이터의 길이를 확인할 변수인 destIndex에 0을 초기화 해준다.
|
|
* 이 전에 ReadAsync()를 통해 Header 부분은 전송을 받았으므로, Body 부분부터 전송을 받을 것이다. 전송받은 위치부터 남은 길이만큼의 byte 데이터를 비동기 처리로 전송받는다.
|
|
* 이 전에 ReadAsync()를 통해 Header 부분은 전송을 받았으므로, Body 부분부터 전송을 받을 것이다. 전송받은 index(destIndex)부터 남은 데이터의 길이(remainLength)만큼의 byte 데이터를 비동기 처리로 전송받는다.
|
|
* Payload의 길이가 남아있는 상태에서 전송받은 데이터가 없으면 오류로 판단하여 -1을 반환한다.
|
|
* 한 번에 많은 데이터를 전송받지 못 하므로 readAmount에 전송받은 데이터의 길이를 저장하여 남은 길이를 확인할 것이다.
|
|
|
|
* 남은 데이터의 길이(remainLength)가 있음에도 전송받은 데이터(readAmount)가 없으면 오류로 판단하여 -1을 반환한다.
|
|
|
|
* 남은 데이터의 길이(remainLength)에서 전송받은 데이터의 길이(readAmount)를 제외한다. 또한, 다음에 전송받은 데이터를 이어서 저장하기 위해 recvBuffer의 index를 확인하는 변수인 destIndex에 전송받은 데이터의 길이(readAmount)를 더해준다.
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
... | | ... | |