반응형

※ 일반적인 쓰레드기반 다중 서버...

서버쪽 쓰레드 하나가 하나의 소켓을 전담한다.

서버쪽 쓰레드가 계속해서 일하는 것이기 아니기 때문에 소켓과 쓰레드가 1:1 대응되는 것은 서버에 부담이 된다. 

1. 리소스 부담... 

2. context switching이 빈번하게 발생하기 때문...


※ 소켓의 수는 줄일 수 없다.

하지만, 하나의 쓰레드가 3개의 소켓을 한꺼번에 담당한다면 리소스 부담 감소, context switching 감소...


22-1. Completion Port 입출력 기본 원리

IOCP란?

1. 기본적으로 비동기 입출력 모델이다. Overlapped IO

2. 제한된 쓰레드의 수를 통해서 여러 소켓의 입출력을 담당하게 한다.

3. 컨텍스트 스위칭에 소비되는 시간을 줄이는 모델이다.

4. Overlapped 입출력 모델의 특징과 비동기 Notification 입출력 모델의 특징을 동시에 지닌다.

원래 비동기 Notification 입출력 모델 : 먼저 확인 후 그 다음 입출력한다.

IOCP모델 : 먼저 입출력 한 후 확인을 나중에 한다.


IOCP 구현모델


Completion Port 내부에 큐가 생성된다. 

연결된 소켓이 입출력이 완료되면 Completion Packet을 만든다.

Completion Packet에는 입출력이 완료한 소켓 정보등에 대한 데이터 묶음이다.

이 packet을 Completion Queue에 집어 넣는다.

쓰레드는 큐에서 packet을 꺼내 내용을 처리한다.


IOCP에서 적절한 쓰레드의 수

Response Session : 서버가 클라이언트로 온 요청을 처리하고 전송하기 직전까지 준비과정.

Response Session이 짧은 경우(간단한 연산 결과를 돌려줌) : CPU의 개수를 초과하지 않는다.

Response Session이 긴 경우 : CPU의 개수를 적절히 초과하여 클라이언트의 평균 만족도를 높인다.

※ 실험적으로 판단하여 평가해야 한다.

일반적인 경우 : CPU의 개수를 초과하지 않는다.


22-2. Completion Port 입출력을 위한 기본 단계

1. Completion Port 오브젝트의 생성

HANDLE CreateIoCompletionPort(

HANDLE FileHandle, // 연결 시킬 소켓 핸들

HANDLE ExistingCompletionPort, // 연결시킬 completion port 핸들

ULONG_PTR CompletionKey, // completion key

DWORD NumberOfConcurrentThreads // 동시 실행 가능한 쓰레드의 수

);

실행예)

HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

※ NumberOfConcurrentThreads가 0이면 CPU의 수만큼 허용


2. Completion Port 오브젝트와 소켓의 연결

completionKey : 입출력이 완료되었을 때 Completion Packet에 저장하는 추가적인 데이터

실행예)

CreateIoCompletionPort(소켓핸들, hCompletionPort, (DWORD)PerHandleData, 0);


3. Completion Queue에 들어 있는 패킷 정보 확인

BOOL GetQueuedCompletionStatus(

HANDLE CompletionPort,  // completion port 핸들

LPDWORD lpNumberOfBytes, // 전송된 바이트수

PULONG_PTR lpCompletionKey, // file completion key

LPOVERLAPPED *lpOverlapped, // buffer

DWORD dwMilliseconds // 선택적인 timeout 값

);

2,3,4 인자를 통해 completion packet의 내용을 얻어온다.


22-3 Completion Port 입출력 기반의 서버구현


CreateCompletionPort의 키 : 완료된 소켓의 정보


Completion Port : 완료감지, completion packet 생성 : key, overlapped, 전송 바이트 수로 구성.

WSARecv의 6번째 인자인 LPWSAOVERLAPPED에서 hEvent를 이용한다. ->

수신된 데이터가 존재하는 버퍼 정보를 담음. overlapped 구조체 뿐만 아니라 다른 구조체도 추가 가능. 



반응형
반응형

21-1. Overlapped 입출력의 의미

비중첩 데이터 입출력 모델


중첩된 데이터 입출력 모델

하나의 쓰레드 내에서 여러 개의 입출력이 진행되는 것


21-2. Overlapped 입출력을 위한 기본 단계

1. Overlapped 소켓의 생성

SOCKET WSASocket(

  __in  int af,

  __in  int type,

  __in  int protocol,

  __in  LPWSAPROTOCOL_INFO lpProtocolInfo,

  __in  GROUP g,

  __in  DWORD dwFlags

);

af : address family

type : 소켓 형태

protocol : 사용된 프로토콜(여기까지 socket과 동일)

lpProtocolInfo : 생성될 소켓의 성경을 정의하는 wSAPROTOCOL_INFO 포인터

g : 예약됨.

dwFlags : 소켓 속성을 지정하는 플래그


WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

WSA_FLAG_OVERLAPPED는 Overlapped 입출력이 가능하도록 하는 플래그.


2. 데이터 송수신

※ 다음 함수들은 버퍼에 존재하는 데이터를 모아서 한 번에 전송하고(Gather) 수신된 데이터를 여러 버퍼에 나누어 저장(Scatter)하는 Gather/Scatter 입출력을 한다.


int WSASend(

    SOCKET s,                  // 소켓 핸들

    LPWSABUF lpBuffers,    // WSABUF 구조체 배열의 포인터

    DWORD dwBufferCount, // lpBuffers가 가리키는 배열의 크기

    LPDWORD lpNumberOfBytesSent, // 전송된 바이트 수를 저장하기 위한 포인터

    DWORD dwFlags,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTING lpCompletionRoutine

);

리턴 : 성공시 0, 실패시 SOCKET_ERROR


typedef struct __WSABUF{

    u_long len;

    char FAR *buf;

}WSABUF, FAR* LPWSABUF;


int WSARecv(

    SOCKET s,                  // 소켓 핸들

    LPWSABUF lpBuffers,    // 수신 버퍼 정보를 지니는WSABUF 구조체 배열의 포인터

    DWORD dwBufferCount, // lpBuffers가 가리키는 배열의 크기

    LPDWORD lpNumberOfBytesRecvd, // 전송된 바이트 수를 저장하기 위한 포인터

    DWORD dwFlags,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTING lpCompletionRoutine

);

리턴 : 성공시 0, 실패시 SOCKET_ERROR

lpOverlapped : 중첩된 입출력을 하기 위해 사용.


3. 데이터 송수신 완료 확인

이벤트 커널 오브젝트 기반

CALLBACK 함수 기반


21-3. Event 커널 오브젝트 기반의 Overlapped I/O
이벤트 커널 오브젝트 : 데이터 송수신이 되었는지 확인하는 용도로 사용.

WSACreateEvent
typedef struct _WSAOVERLAPPED{
    DWORD Internal;
    DWORD InternalHigh;
    DWORD Offset;
    DWORD OffsetHigh;
    // 위의 4개 변수는 내부적으로 사용.
    WSAEVENT hEvent;    // 이벤트 핸들의 오브젝트를 저장한다.
}WSAOVERLAPPED, *LPWSAOVERLAPPED;

WSAWaitForMultipleEvents 함수는 이벤트가 시그널 되었는 지 확인한다.
WSAGetOverlappedResult(
    SOCKET s,
    LPWSAOVERLAPPED lpOverlapped, // WSASend 혹은 WSARecv 호출시 전달한 OVERLAPPED 구조체
    LPDWORD lpcbTransfer, // 전송된 바이트 수
    BOOL fWait,    // TRUE 전달 시 입출력 완료시까지 블로킹
    LPDWORD lpdwFlags
);
※ 송수신한 바이트수, 에러가 발생하였는가? 의 결과를 알려준다.

1. 소켓 이벤트와 WSAOVERLAPPED 구조체와 연결하는 방법
event = WSACreateEvent();
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = event;
2. WSAWaitForMultipleEvents 함수 호출
WSAWaitForMultipleEvents(1, &event, TRUE, WSA_INFINITE, FALSE);
3. WSAGetOverlappedResult 함수 호출
WSAGetOverlappedResult(hSocket, &overlapped, &sendBytes, FALSE, NULL);

21-4. Completion Routines 기반의 Overlapped I/O
콜백 함수 : 특정 상황이 되면 시스템에서 의해 호출되는 함수.

LPWSAOVERLAPPED_COMPLETION_ROUTINE : 다음 콜백함수에 대한 포인터
void CALLBACK CompletionROUTINE(
    DWORD dwError,    // 오류 정보 전달 됨.
    DWORD cbTransferred,    // 전송된 바이트 수 전달됨
    LPWSAOVERLAPPED lpOverlapped,    // WSARecv 함수 호출시 전달한 구조체 변수
    DWORD dwFlags
);
콜백함수가 있을지라도 WSAOVERLAPPED 구조체는 전달해 주어야 한다.
그래서 이벤트 커널 오브젝트는 Dummy Object가 된다.

index = WSAWaitForMultipleEvents(1, &event, FALSE, WSA_INFINITE, TRUE);
index가 WAIT_IO_COMPLETION이면 중첩된 입출력이 완료되었다는 뜻.
다른 의미는 오류가 발생함.
반응형
반응형

foreach 반복문

foreach(object obj in list)

{

    DoSomthing(obj);

}

효과적이고 사용하기 쉬움


foreach문을 지원하는 타입

1. 기존타입(Array,List..)을 상속하여 사용

2. 새로운 타입 선언 가능 -> 인터페이스 구현


foreach를 지원하는 타입 정의(C#1.0)

IEnumerator e = list.getEnumerator();    // 특정 메소드를 선언하고 있다고 가정한다.

while(e.MoveNext())     // 다음 한 칸 이동하고 이동가능한지 리턴.

{

    object obj = e.Current;

    DoSomething(obj);

}

※ foreach를 지원하기 위해서는 다음 인터페이스를 구현하면 된다.

1. IEnumerable

public interface IEnumerable{

    IEnumerator GetEnumerator();

}

2. IEnumerator

public interface IEnumerator{

    object Current {get;}    // object로 되어 있기 때문에 boxing, unboxing필요...

    bool MoveNext();        // 어떠한 순서로 돌아가는 지 정의... 한가지 형태의 반복만 가능.

    void Reset();

}


※ yield return을 통해서 컴파일러 수준에서 Enumerator를 만들어준다.

public IEnumerator GetEnumerator()

{

    for(int i=0; i <  Cities.Length; i++)

    {

        yield return Cities[i];

    }

}


※ IEnumerable<string>와 같이 Generic을 이용하면 속도가 더 빠를 수 있다. boxing, unboxing없음.


Iterator 구문(2.0)

반복문 내부에서 yield 키워드 사용

C# 컴파일러가 iterator를 대신 구현해줌.

IEnumerable과 IEnumerator의 구체적인 구현 생략가능.

Generics 버전 인터페이스 : IEnumerable<T>, IEnumerator<T>


Iterator 주의사항

중첩 foreach문 사용자제 -> index를 이용한 for 반복문 사용 권장.

반응형

'C#' 카테고리의 다른 글

Nullable Type  (0) 2009.09.07
Generic  (0) 2009.09.07
Partial Class  (0) 2009.08.29
C# 3장 간단 정리  (0) 2009.07.30
C# 2장 간단 정리  (0) 2009.07.30
반응형

※ 일반적으로 Value타입은 null이 없음. Reference타입은 null은 있음.

※ 필요한 사전지식 : 값/참조형식의 의해

Nullable 타입

Value타입(값 형식)에 NULL값을 설정할 수 있다는 새로운 형태의 Value타입(값 형식)

예) int i = null;

나오게 된 배경

Database 시스템과 프로그래밍 언어의 자료처리의 차이점

Database 

모든 필드는 제약조건의 설정에 따라 null일 수 있다.

OOP 언어

데이터 타입은 값 형식과 참조 형식으로 구분됨.

값 형식은 null을 가질 수 없고 기본 값을 가짐.

참조형식은 null을 가질 수 있음.


데이터조회 예시

C#1.0의 예시

int iAge;

...

if( !dbReader.IsDBNull(dbReader.GetOrdinal("Age")) )

   iAge = System.Convert.ToInt32(dbReader["Age"]);

else

    iAge = ???; // 실제 DB에 값이 null일 경우? 보통 -1로 표현하기도 함.

C#2.0

int? iAge;

if( !dbReader.IsDBNull(dbReader.GetOrdinal("Age")) )

   iAge = System.Convert.ToInt32(dbReader["Age"]);

else

    iAge = null; // 값에 null을 표현 가능하다!


int otherValue = iAge ?? 0;  // null이면 0으로 할당.


주의사항

값 형식에만 사용.

예시)

string? Message = "Hello, World!"; // compile error

MyClass? myClass; // compile error

형변환

int? x = null;

int y = x;          // compile error

int y = (int)x;    // null일 경우 Runtime Error!! -> InvalidOperationException

int y = x.Value; // null일 경우 Runtime Error!! -> InvalidOperationException

int y = x ?? 0;   // 좋은 선택~


타입 정의방법

int i;

System.Int32 i = 0; // Common type system(CTS) 여러 언어에 대해...

int? x1 = 123;

System.Nullable<int> z1 = 10;    // CLR 타입. 타입 제약조건으로 값형식만 받을 수 있다.


if(i1==null) i = 0; else i = (int)i1;   // i1.HasValue를 이용할 수 있음.


값 형식과의 연산

int? i3 = null;

int? j3 = i3 + 100; // 결과는 null, null+-의 결과는 null

null에 대한 출력값은 '빈 문자열'이다.


Nullable 타입 실제 구현 예시

struct MyNullable<T> where T:struct

{

    public T value;

    private bool IsNull;

    public bool HasValue

    {

        get

        {

            return !IsNull;

        }

    }


선언방법

선언 : T?

System.Nullable<T>

null 할당 가능 int? x = null;

null 확인

int? x;

...

if(x.HasValue) or if(x!=null)

?? Operator

int? x;

int y = x ?? 0;

값 형식과의 연산

int? x;

int? y = x + 1; // x가 null이면 y도 null

반응형

'C#' 카테고리의 다른 글

Iterator  (0) 2009.09.07
Generic  (0) 2009.09.07
Partial Class  (0) 2009.08.29
C# 3장 간단 정리  (0) 2009.07.30
C# 2장 간단 정리  (0) 2009.07.30
반응형

Generic(제너릭)

사전적 정의 : 일반적인, 포괄적인, 통칭의, 범용


screwdriver set as a generic tool

※ (드라이버 꼭지)를 바꿈으로서 다양하게 사용할 수 있다.


※ Professional .NET 2.0 Generics이라는 책이 존재할 정도로 방대한 분야.


장점

Type checking, boxing, casting 등의 작업 불필요 => 성능 향상

사용 용도

Custom List, Collection 등을 쉽게 구현


object List 정의

public class List

{

    private object[] elements;

    private int count;


    public void Add(object element){ ... }

    public object this[int index]{ get... set... }

    public int Count { get... }

}


object List 사용 예시

List intList = new List();

intList.Add(1);           // boxing 실행됨.

intList.Add(2);

intList.Add("Three");  // 이후에 Runtime시 잠재적 에러 가능성. (배열 형태기 때문에)

int i = (int)intList[0];   // 형 변환 필요.


요소의 타입이 object인 List 클래스

Type Casting 필요

에러 발생시 run-time 에러

값 형식이 저장되면

boxing/unboxing 호출됨 -> 성능 저하

※ 범용적으로 사용될 수 있으나 최선의 방법은 아니다.


C# 1.0 해결책

Strong Typed List 생성

 - intList, stringList, myClassList

문제점 

각각의 타입마다 코드생성 -> 관리의 어려움

각각의 코드는 상당히 유사함


C# 2.0 해결책

코드상 generic

정의(define) : 특정 logic을 구현하는 것. 함수 정의, 클래스 정의 등.

코드의 사용(reference) : 구현된 로직을 사용.

List, Collection, Queue, Stack 등의 모든 자료 구조에 적용가능.


Generic type 정의

public class List<T>

{

    private T[] elements;

    private int count

    public void Add(T element){ ... }

    public T this[int index] { ... }

    public int Count{ ... }

}

Generic type 사용

List<int> intList = new List<int>();

intList.Add(1);               // boxing 발생하지 않음.

intList.Add(2);

intList.Add("Three");      // 컴파일 에러 발생

int i = intList[0];            // 형변환 필요없음.

※ int가 type parameter에 대입되는 효과를 가진다.


T = type parameter

T는 타입 자리에 들어간다. 

클래스 부분에 <T>라고 써 준다.

※ Generic으로 만들어진 코드는 사용하면서(reference) 완성된다.


예시)

void swap<T>(ref T a, ref T b)

{

    T c;

    c = a;

    a = b;

    b = c;

}


type parameter에 대해 constraint 적용이 가능하다.

예시)

void swap<T>(ref T a, ref T b) where T:struct

object에 대해 적용 안되며 일반 타입에 대해서만 적용되는 함수를 만든다.


정의

코드의 특정부분을 구체적으로 정의(define)하지 않고 사용(reference)하는 것

적용 코드 부분 : class, interfaces, method

특정부분(정의하지 않는 부분)

<T> : Type Parameter

코드 정의 : type은 <T>와 같이 일반적(Generic)으로 정의

코드 사용 : 명시적 <T>를 정의


Framework에 정의되어 있는 Generic 코드

Collection classes

List<T>, Dictionary<TKey, TValue>, SortedDictionary<TKey, TValue>, Stack<T>, Queue<T>, LinkedList<T>

Collection interfaces

IList<T>, IDictionary<TKey, TValue>, ICollection<T>, IEnumerable<T>, IEnumerator<T>, IComparable<T>, IComparer<T>

Collection base classes

Collection<T>, KeyedCollection<T>, ReadOnlyCollection<T>

Utility classes

Nullable<T>, Comparer<T>

Reflection


장점

타입 안정적(=strong typed) : No more object~ 

성능향상 

Object안에 Integer를 넣었을 때 Generic이 3배 빠름.

Object안에 String를 넣었을 때 Generic이 20% 빠름.

효율적인 코드관리

코드 재사용 

polymorphism : interface, class override, delegate, generic

IDE Tools 지원


실제 코드의 완성은 사용(refererence)하면서 됨.

CLR 기능

runtime시 동작

VB.NET, C++.NET등의 언어에서도 지원

C++의 탬플릿과 개념은 유사하나 기능상으로 많이 다름.

자바의 Generic은 Boxing/Unboxing이 항상 수행된다는 점에서 .NET버젼이 더 우수하다.


반응형

'C#' 카테고리의 다른 글

Iterator  (0) 2009.09.07
Nullable Type  (0) 2009.09.07
Partial Class  (0) 2009.08.29
C# 3장 간단 정리  (0) 2009.07.30
C# 2장 간단 정리  (0) 2009.07.30
반응형

Partial classes

Generics

Nullable types

Iterators

//Anonymous types

//static classes

//different accessibility for property accessors

//External Aliases

//Namespace Alias Qualifiers

//Inline Warning Control

//...


Partial classes

하나의 클래스 -> 두 개 이상의 파일에 분리되어 존재가능

public partial class Customer

{

}


소스 컨트롤 : 파일 단위로 checkin, checkout함.

하나의 클래스를 여러 명이 작업해야 한다면?

하나의 클래스를 개발자수만큼 분리 후 컨트롤.

Code Generation : 툴이 생성한 코드와 개발자 코드의 분리.

예시)

Form1.cs과 Form1.Designer.cs partial 형태 두개로 나누어 놓았음


예시) 개발자 코드가 추가되면 안되는 코드

디자이너 코드

웹서비스 프록시 코드

Typed DataSet(xsd)


Partial classes 특징

CLR 수준이 아닌 C# compiler 수준의 기능.

실제 컴파일시에는 하나의 클래스로 인식.

단일 코드로 취합되므로 모든 제약사항은 이에 준함.

 - Base class : 모든 patrial 클래스는 같은 상위 클래스를 상속받아야 함.

 - Attribute, Interface 등도 동일한 개념.

binary 수준에서 partial이 허용되지 않는다. 소스코드 수준에서 partial 가능.


Patrial classes 용도

소스 컨트롤

Tool이 생성한 코드에 사용자 코드 추가.


무분별한 partial 클래스의 사용

코드의 관리를 어렵게 할 수 있다.

하나의 클래스 -> n개의 소스 파일


클래스뷰의 적절한 사용

partial의 유무와 관계없이 일관된 클래스 정보 표시.


반응형

'C#' 카테고리의 다른 글

Nullable Type  (0) 2009.09.07
Generic  (0) 2009.09.07
C# 3장 간단 정리  (0) 2009.07.30
C# 2장 간단 정리  (0) 2009.07.30
C# 1장 간단 정리  (0) 2009.07.30
반응형

1. 비동기 Notification의 의미

인터넷 계층

Link계층 : 물리 + 데이터링크

IP계층

TCP/UDP계층 : 송수신 버퍼가 생성된다.

응용프로그램 계층


동기화된 입력 및 출력 함수의 호출


동기화 : 함수의 호출과 행위의 시작, 함수의 리턴과 행위의 종료가 일치함.

데이터 전송 종료 :  Application -> TCP계층으로 데이터를 다 보낸 상황. send함수를 호출하면 출력버퍼로 보내고자 하는 데이터가 다 들어갔을 때의 상황. 

데이터 수신 종료 : TCP -> Application으로 데이터를 다 읽은 상황.


동기화된 데이터 입출력 과정상의 문제점


※ Host B의 버퍼가 꽉 찼을 경우에 (패킷 전송 일시 정지 요청에 의해) Host A는 패킷 전송을 하지 않고 blocking상태에 있게 된다.

※ send함수에서 패킷을 다 보냈을 경우 send함수가 리턴되어 종료된다.


비동기화 Notification

동기화 Notification

1. select 함수를 기반으로 한 서버의 구현

2. 핸들에 변화가 발생해야 리턴한다. select 함수의 리턴 시기와 핸들 변화 시기가 일치(동기)

비동기화 Notification

1. WSAEventSelect 함수를 기반으로 한 서버의 구현

2. 함수의 호출과 동시에 리턴한다. 따라서 핸들의 변화를 확인하기 위한 별도의 과정이 필요하다.


20.2 WSAEventSelect 모델 기반 서버 구현을 위한 API

구현순서


※ 소켓과 소켓을 위한 Event Object(소켓의 상태를 반영)가 쌍을 이룬다. 소켓의 변화가 다양하기 때문에 Event 객체가 따로 필요하다.

1. WSACreateEvent : Event를 편하게 이용할 수 있도록 돕는다.

2. WSAEventSelect

select함수 : 관심을 두고자 하는 핸들을 설정(설정) + 소켓의 변화(알림), 동기화 알림 함수.

WSAEventSelect : 관심을 두고자 하는 하나의 핸들을 설정만 할 수 있음.

3. WSAWaitForMultipleEvents : 소켓의 변화를 확인. nonsignaled에서 signaled로 변화할 때 리턴됨.

4. WSAEnumNetworkEvents : 소켓이 변화된 이유를 확인.


필요한 함수

WSAEVENT WSACreateEvent(void);

리턴 : manual-reset 모드, non-signaled 상태의 이벤트


int WSAEventSelect(

    SOCKET s,                       // 감시 대상이 되는 소켓

    WSAEVENT hEventObject, // 소켓의 변화를 확인하기 위한 이벤트 오브젝트의 핸들.

    long lNetowrkEvents         // 감시하고자 하는 이벤트 종류를 Bit-wise OR(|)로 묶어서 전달

);

리턴 : 성공시 0, 실패시 SOCKET_ERROR


감시하고자 하는 이벤트 종류

 이벤트 의미 
 FD_READ  수신된 데이터가 존재할 경우 
 FD_WRITE 블로킹되지 않고 데이터를 전송할 수 있는 경우 
 FD_OOB OOB 메세지 수신에 대한 이벤트
 FD_ACCEPT 연결 요청이 수신되었을 경우
 FD_CLOSE 연결 종료 요청이 수신되었을 경우


DWORD WSAWaitForMultipleEvents(

    DWORD cEvents,                           // 검사 대상의 핸들 수     

    const WSAEVENT FAR *lphEvents, // 검사 대상의 배열

    BOOL fWaitAll,                              // FALSE일 경우 하나의 배열만 변화가 있어도 리턴. TRUE면 모든 배열의 변화가 있어야 함.

    DWORD dwTimeout,                     // 타임아웃.

    BOOL fAlertable

);

여러 개의 이벤트 핸들(포인터로 배열 전달)이 signaled 상태가 되었는지 감시하는 함수.

리턴 : 실패시 WAIT_FAILED 리턴, 성공시 이벤트 발생 오브젝트 정보 리턴


int WSAEnumNetworkEvents(

    SOCKET s,                                                   // 소켓

    WSAEVENT hEventObject,                             // 이벤트가 발생한 소켓과 연결되어 있는 이벤트 오브젝트 핸들

    LPWSANETWORKEVENTS lpNetworkEvents    // 발생한 이벤트 정보와 오류 정보로 채워진 WSANETWORKEVENTS 구조체 포인터

);

이벤트 핸들이 발생한 이유를 알아내는 데 사용한다. signaled 상태의 이벤트 핸들을 nonsignaled상태로 바꿔주는 기능도 수행한다.

리턴 : 성공시 0, 실패시 SOCKET_ERROR 리턴


발생한 이벤트 정보와 오류 정보로 채워진 WSANETWORKEVENTS 구조체

typedef struct _WSANETWORKEVENTS{

    long lNetworkEvents;

    int    iErrorCode[FD_MAX_EVENTS];

} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;


※ bind와 listen 사이의 소켓에 대해 이벤트를 만든다.

index = WSAWaitForMultipleEvents(sockTotal, hEventArray, FALSE, WSA_INFINITE, FALSE);

index = index - WSA_WAIT_EVENT_0;

여러 개의 소켓에서 변화가 일어났는 지 확인하고 소켓의 index값을 얻는다. 리턴된 index값은 가장 작은 소켓 인덱스 값이다. 

변화가 일어난 소켓이 여러개일 수 있기 때문에 각각을 for문을 통해 확인한다.


반응형
반응형

※ 커널 오브젝트가 생성될 때는 non-signaled 상태에 있다가 종료하는 경우에 signaled상태로 바뀐다.

※ nonsignaled상태이면 WaitForSingleObject가 멈추고 그렇지 않으면 통과(리턴)할 수 있다.


19-1. 쓰레드 동기화 기법의 분류

프로세스 실행의 두 가지 모드

사용자 모드(User Mode)와 커널 모드(Kernel Mode)


user mode : 시스템 리소스를 제외하고 원하는대로 사용가능. 

접근할 수 있는 메모리 공간이 제한되어 있음.

물리적 영역으로 직접 접근이 허용되지 않는다.

사용자 모드 동기화 기법 : CRITICAL_SECTION 오브젝트 사용

장점 : 프로그래밍하기 수월, 속도가 빠르다.

단점 : 커널 모드 동기화 기법에 비해 제한된 기능만 가진다.


kernel mode : 시스템 리소스를 접근할 수 있지만 시스템 콜을 통해서만 운영체제에서 직접 처리한다

시스템의 모든 메모리 영역으로 접근 가능.

시스템 리소스 : 파일, 쓰레드

시스템의 안전성을 보장하기 위해서 제공

커널 모드 동기화 기법 : Event, Semaphore, Mutex (다른 프로세스에서 접근 가능)

장점 : Deadlock 문제를 막을 수 있다. 둘 이상의 프로세스 내에 존재하는 쓰레드 간의 동기화 가능. 

단점 : 실행 속도의 저하가 발생.


19-2. CRITICAL_SECTION

동기화 관련 함수

※ lpCriticalSection : CRITICAL_SECTION 변수의 포인터

CS 초기화 : void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

CS 소유(임계영역에 들어갈 때) : void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

CS 반환(임계영역을 나올 때) : void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

CS 소멸 : void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);


커널 오브젝트의 상태

뮤텍스

signaled 상태 -> 뮤텍스 소유가 가능한 상태

non-signaled 상태 -> 뮤텍스가 이미 가지고 있는(소유되어진) 상태

세마포어

signaled 상태 -> 세마포어 카운트가 0이 아닌 경우.

non-signaled 상태 -> 세마포어 카운트가 0인 경우.


BOOL CloseHandle(HANDLE hObject);

커널 오브젝트 객체를 소멸한다. 참조카운트 1 감소.


19-3. Mutex(Mutual Exclusion)

HANDLE CreateMutex(

    LPSECURITY_ATTRIBUTES lpMutexAttributes,

    BOOL bInitialOwner,

    LPCTSTR lpName

);

뮤텍스 객체를 생성하고 참조카운트 1 증가.

리턴 : 성공시 생성된 Mutex 오브젝트 핸들, 실패시

lpMutexAttributes : 보안설정

bInitialOwner : false는 signal, true는 non-signal상태로

lpName : 객체 이름


※ WaitSingleObject함수는 뮤텍스를 가져오는(lock) 함수

BOOL ReleaseMutex(

    HANDLE hMutex

);

Mutex를 돌려주는(unlock)하는 함수.

리턴 : 성공시 TRUE 실패시 FALSE

non-signaled -> signaled


Mutex를 통한 쓰레드 동기화


19-4. Semaphore

세마포어가 0이 되어야 세마포어 오브젝트 non-signaled 상태가 된다.

HANDLE CreateSemaphore(

    LPSECURITY_ATTRIBTE lpSemaphoreAttributes,

    LONG lInitialCount,

    LONG lMaximumCount,

    LPCTSTR lpName

);

세마포어 객체를 생성하고 참조카운트 1 증가.

리턴 : 성공시 생성된 Mutex 오브젝트 핸들, 실패시

lpMutexAttributes : 보안설정

lInitialCount : 초기 세마포어값.

lMaximumCount : 세마포어의 최대값.

lpName : 객체 이름


※ WaitForSingleObject는 세마포어 값을 감소시킨다. signaled상태 -> nonsignaled상태로

BOOL ReleaseSemaphore(

    HANDLE hSemaphore,

    LONG lReleaseCount,

    LPLONG lpPreviousCount

);

리턴 : 성공시 TRUE, 실패시 FALSE

hSemaphore : 세마포어의 핸들.

lReleaseCount : 세마포어 값을 증가시킬 크기

lpPreviousCount : 이 함수를 호출하기 전에 카운트 값을 저장할 수 있다.


19-5. Event

Event 특징 : auto & manual-reset 모드의 커널 오브젝트의 생성이 가능하다.

※ 대기 중에 있는 쓰레드들을 한꺼번에 깨울 수 있다. (장점)

HANDLE CreateEvent(

    LPSECURITY_ATTRIBUTES lpEventAttributes,

    BOOL bManualReset,

    BOOL bInitialState,

    LPCTSTR lpName

);

이벤트를 생성한다.

리턴 : 성공시 생성된 Event 오브젝트 핸들. 실패시 NULL

lpEventAttributes : 보안설정

bManualReset : TRUE면 manual-reset 모드의 이벤트 생성. 

bInitialState : TRUE면 signaled 상태의 이벤트 생성, FALSE면 non-signaled 상태의 Evenet가 생성

lpName : Event에 이름을 준다.


manual-reset 모드 Event 동기화 원리

1. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); -> 쓰레드 생성하면 대기상태로 생성.

2. SetEvent(hEvent);

3. NumberOfA, NumberOfOthers가 수행됨.

반응형
반응형

18-1 커널 오브젝트(Kernel Objects)

커널 오브젝트 

시스템 리소스의 정보를 담고 있는 데이터 블록(윈도우즈 기반에서만)

시스템(운영체제)이 관리한다.


18-2. 윈도우즈 기반의 쓰레드 생성


#include<process.h>

unsigned long _beginthreadex(

    void *security,

    unsigned stack_size,

    unsigned (*start_address)(void*),

    void *arglist,

    unsigned initflag,

    unsigned *thrdaddr

);


security : 보안에 관련된 설정

stack_size : 쓰레드 생성시 요구되는 스택의 크기. 0이면 default

start_address : 쓰레드에 의해 호출되는 함수의 포인터

arglist : start_address가 가리키는 함수 호출시, 전달할 인자를 지정해 준다.

initflag : 바로 실행상태(0)가 되느냐, 아니면 대기 상태로 들어가느냐 결정하는 요소. 

thrdaddr : 쓰레드 생성시 쓰레드 ID


쓰레드 생성을 위한 라이브러리 설정


※ 윈도우즈도 main thread가 먼저 끝나면 다른 thread들이 실행중이라도 프로그램이 종료가 된다.

※ 윈도우즈는 기본 실행 단위가 thread다. 프로그램을 만들면 main thread(main함수 실행하는 용도)가 생성된다.


커널 오브젝트, 리소스, 핸들과의 관계


※ 프로그램은 파일을 직접 조작하는 것이 아니라 커널 오브젝트에게 원하는 조작을 요청하고 시스템이 파일을 조작하게 된다.


18-3. Signaled & Non-Signaled 커널 오브젝트

커널 오브젝트의 상태

쓰레드 커널 오브젝트의 상태

생성시 : non-signaled 상태로 초기화.

종료시 : signaled 상태로 변경.


커널 오브젝트 상태 확인 함수

#include<windows.h>

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);

리턴 : signaled 상태가 되어 리턴하는 경우 WAIT_OBJECT_0, 타임아웃에서 리턴될 경우 WAIT_TIMEOUT. 오류시 WAIT_FAILED.

hHandle : 상태를 확인할 커널 오브젝트의 핸들을 전달.

dwMilliseconds : 타임아웃을 1/1000초 단위로 설정한다. INFINITE를 전달할 경우 signaled 상태가 되기 전에 절대 리턴하지 않는다.

※ signaled 상태가 될 때까지 main thread가 기다려주면 된다. 리눅스의 JOIN과 비슷한 효과.


커널 오브젝트의 종류

auto-reset mode 커널 오브젝트 : 리턴이 되면서 커널 오브젝트가 non-signaled 상태가 됨.

manual-reset mode 커널 오브젝트 : 리턴이 되어도 커널 오브젝트는 자동으로 상태가 변경되지 않음.

반응형
반응형

17-1. 프로세스(Process)와 쓰레드(Thread)

쓰레드란 무엇인가?

경량화된 프로세스 

동시 실행 가능.


프로세스와 차이점

스택, PC를 포함한 레지스터 복사본, 쓰레드 지역 보관영역을 제외한 나머지 메모리 영역을 공유.

※ fork를 통해 프로세스 복사했다면 부모와 자식 프로세스는 서로 독립적으로 작동한다.

data : 전역변수가 저장되는 공간.

heap : 동적할당을 하는 메모리 공간.

stack : 지역변수를 위해 사용하는 공간.

보다 간단한 컨텍스트 스위칭.

일부 메모리를 공유하므로 쓰레드간 통신이 편리.

(프로그래밍 하는데 부담이 될 수 있음.)


17-2. 쓰레드 생성하기

쓰레드란 무엇인가?

프로세스(LINUX) : 실행되고 있는 일의 단위

쓰레드 : 프로세스 내에서 다시 나누어지는 일의 단위. 프로세스가 존재해야 쓰레드 생성이 가능.


#include<pthread.h>

int pthread_create(pthread_t * thread, pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

쓰레드를 생성한다. nonblocking함수.

리턴 : 성공시 0, 실패시 이외 값.

thread : 생성된 쓰레드의 ID를 저장할 변수의 포인터.

attr : 생성하고자 하는 쓰레드의 특성. 일반적으로 NULL을 전달.

start_routine : 리턴타임이 void*이고 인자도 void*인 함수를 가리키는 포인터

arg : 쓰레드에 의해 호출되는 함수에(start_routine 포인터가 가리키는 함수) 전달하고자 하는 인자 값.


※ main쓰레드가 먼저 죽으면 자식 쓰레드는 끝까지 실행되지 않고 종료될 수 있다.


#include<pthread.h>

int pthread_join(pthread_t th, void **thread_return);

리턴 : 성공시 0, 실패시 이외 값.

th : 인자로 들어오는 ID의 쓰레드가 종료할 때까지 실행을 지연시킨다. blocking 함수.

thread_return : 쓰레드가 종료 시 반환하는 값에 접근할 수 있는 2차원 포인터.


임계영역과 쓰레드에 안전한 함수

임계영역 : 두 개 이상의 쓰레드에 의해서 동시에 실행되면 안 되는 영역.

쓰레드 안전한 함수(Thread safe function) : 임계영역에서 호출이 가능한 함수.

예) gethostbyname(쓰레드 불안전한 함수) vs gethostbyname_r(쓰레드 안전한 함수)


gcc -D_REENTRANT thread2.c -o thread2 -lpthread

-D_REENTRANT : #define _REENTRANT의 뜻. 쓰레드 불안전한 함수를 쓰레드 안전한 함수로 바꿔주는 매크로.

-lpthread : 기본적으로 쓰레드와 링크되어 있지 않기 때문에 링크시키기 위한 옵션.


17-3. 임계영역 & 쓰레드의 문제점
컴퓨터가 덧셈을 하는 기본 원리

※ 임시메모리 = accumulator

두 개의 쓰레드에 의한 덧셈 연산
int i = 10;    // 전역 변수
....
i+=10;

※ A Thread가 끝나기 전에 B Thread가 수행될 수 있다.

※ 3단계에서 i=30; 이 되어야 하지만 i=20;이 되어 버렸다.


17-4. 동기화(Synchronization)

임계영역 : 둘 이상의 쓰레드에 의해서 공유되는 메모리 공간에 접근하는 코드 영역.

동시에 접근 가능한 메모리(전역변수)에 접근해서 연산하는 코드 영역

동기화

1. 공유된 메모리에 둘 이상의 쓰레드가 동시 접근하는 것을 막는 행위.

2. 둘 이상의 쓰레드 실행순서를 컨트롤(control)하는 행위.

동기화 기법 : 뮤텍스, 세마포어


17-5. 뮤텍스(Mutex)

뮤텍스의 정의

Mutual Exclusion의 줄이말로 쓰레드 서로 간에 동시 접근을 허용하지 않겠다는 의미.

pthread_mutext_t 타입의 변수를 가리켜 뮤텍스라고 한다.

일종의 (화장실) 문고리에 해당.

뮤텍스의 기본 원리

임계영역에 들어갈 때 뮤텍스(문고리)를 잠그고 들어간다.

임계영역에서 나올 때 뮤텍스를 풀고 나간다.

뮤텍스를 조작하는 함수들

#include<pthread.h>

초기화 : pthread_mutex_init

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

뮤텍스 소멸 : pthread_mutex_destroy

int pthread_mutex_destroy(pthread_mutex_t *mutex);

뮤텍스 잠금 : pthread_mutex_lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

뮤텍스 풀기 : pthrea_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex);


뮤텍스 동기화 원리


17-6. 세마포어(Semaphore)

세마포어의 정의

sem_t 타입의 변수를 가리켜 흔히 세마포어라 표현한다.

세마포어의 기본 원리

세마포어는 정수를 지닌다. 정수의 값이 0이면 실행 불가를 의미한다.

세마포어가 1이상이 되면 실행 가능을 의미한다.

세마포어는 0미만이 될 수 없지만 1이상은 될 수 있다.

바이너리 세마포어 : 0과 1의 상태만을 지닌다.

세마포어를 조작하는 함수들

#include<semaphore.h>

세마포어 초기화 : sem_init

int sem_init(sem_t *sem, int pshared, unsigned int value);

value : 초기화하고자 하는 값.

세마포어 소멸 : sem_destroy

int sem_destroy(sem_t * sem);

세마포어 감소 : sem_wait

int sem_wait(sem_t * sem);

세마포어 증가 : sem_post

int sem_post(sem_t * sem);


세마포어 동기화 원리

Thread A는 새로운 데이터를 주고 Thread B는 그 데이터를 받는다.

a->b->a->b 순서가 반복된다.


17-7. 쓰레드 기반 서버 구현하기

채팅 서버의 임무 : 전체 클라이언트에게 메세지 내용을 다 보내야 되기 때문에 클라이언트 소켓을 갱신해야 한다.


pthread_mutex_lock(&mutx);

for(i=0;i<clnt_number;i++)

    write(clnt_socks[i], message, len);

pthread_mutex_unlock(&mutx);

위 소스에서 clnt_socks[i]에서 클라이언트 소켓 파일 디스크립터가 존재한다고 생각하고 write함수를 호출하지만 파일 디스크립터가 사라질 위험이 있기 때문에 mutex를 통해 잠가 놓는다.



반응형

+ Recent posts