Process Manipulation#

프로세스란 실행되고 있는 프로그램을 말한다(자세한 내용은 다른 문서를 참고한다). 이런 프로세스는 여러가지로 쓰임새가 많다. 대표적인 예로, 데몬을 들 수 있다. 데몬은 사용자 모르게 내부적으로 수행하며 각종 작업을 처리한다. Windows에서 좋은 예는 explore, svchost, winlogin 등을 들 수 있다. 특히 svchost는 사용자가 실행하기를 원하는 각종 서비스를 수행하는 데몬이다. Linux는 inetd등과 같은 데몬이 있다. 이와 같이 데몬은 여러모로 유용하게 쓰이는데, 이러한 프로세스 다루기의 정점은 Semapore, Lock 등을 통한 Critical Section 관리와 프로세스간 Communication이다.

이 문서에서는 간단하게 데몬을 생성하는 법을 다룬다.

 

Daemon#

먼저 데몬에 대해서 간략하게 설명하면, 데몬이란 독립적으로 실행되는 프로세스이다. 이런 프로세스가 되기 위해서는 다음과 같은 과정을 거친다.

  1. 부모 프로세스를 실행한다.
  2. 자식 프로세스가 생성된다.
  3. 자식 프로세스를 독립화 하고 작업을 할당한다.
  4. 부모 프로세스는 자식 프로세스가 종료하기를 기다리지 않고 종료한다.
  5. 자식 프로세스는 데몬화 하여 동작한다.

이러한 과정을 거쳐 데몬이 동작하게 된다. 이런 데몬 프로세스를 작성하는 방법은 운영체제마다 틀리며,각 운영체제에 적합한 방법을 사용해야 한다. 본 문서는 Linux 환경에서 데몬을 생성하는 방법에 대해서 다룬다.먼저 부모 프로세스를 생성하기 위해서는 fork()를 사용하며, 독립화 하기위해서 setsid()를 사용한다. 1번 과정에서는 fork()를 실행하여 부모 프로세스를 만들고, setsid()를 사용하여 앞으로 생성될 자식 프로세스를 독립화한다. 이후, 자식 프로세스를 하나 생성하고 작업을 할당한 후에 프로그램과 부모 프로세스는 종료한다.

 

Cron Table#

사실 cron table을 사용하면 root 계정에서 특정 user 계정으로 일정 주기별로 프로그램 실행이 가능하다. 이는 Daemon을 굳이 작성하지 않아도 된다는 이야기이다.즉, 일정 주기를 가지고 작업이 실행된다면 굳이 데몬을 작성하지 않고 데몬과 유사하게 프로그램을 수행할 수 있다. 하지만 이 경우 단점은 cron을 root만 설정할 수 있기 때문에 일반 사용자가 접근하기 힘들다. 따라서 이 부분은 배제 하였다.

 

Create Daemon#

Daemon절에서 언급한 내용을 Python으로 구현하면 아래와 같다. Sample 클래스는 Daemon 클래스 테스트 용도로 작성되었으며 실제 구현 코드는 Daemon 클래스이다. Daemon 클래스를 사용하기 위해서는 아래 __main__ 프로시저에서 사용한 것과 같이 therad를 하나 생성하여 인자로 넘겨주면 된다

  1. #!/usr/bin/python
    # -*- coding: utf-8

    import os, time, sys, threading

    class NullDevice(object):
        def write(self, s): pass

    class SampleJob(object):
        def Main(self):
            logfile = open('sample.log', 'a')
            id = 0
            while True:
                logfile.write('log : %d\n' % id)
                logfile.flush()
                time.sleep(10)
                id = id + 1

    class Daemonize(object):
        def __init__(self):
            pass
        def initDaemon(self):
            # stdout, stderr, stdin closing
            sys.stdout = NullDevice()
            sys.stderr = NullDevice()
            sys.stdin.close()

        def createDaemon(self, job):
            self.initDaemon()
            try:
                pid = os.fork()
                if pid > 0:
                    sys.exit(0)
            except OSError, e:
                print >> sys.stderr, "fork #1 failed : %d (%s)" % \
                    (e.errno, e.strerror)
                sys.exit(1)

            os.setsid()
            os.umask(0)

            try:
                pid = os.fork()
                if pid > 0:
                    print "Daemon PID %d" % pid
                    sys.exit(0)
            except OSError, e:
                print >> sys.stderr, "fork #2 failed : %d (%s)" % \
                    (e.errno, e.strerror)
                sys.exit(1)

            job.start()

    if __name__ == '__main__':
        smjob = SampleJob()
        dm = Daemonize()
        th = threading.Thread(target=smjob.Main(), args=())     dm.createDaemon(th)
        sys.exit(0)

 

Resource Manipulation

이 절은 자원관리의 중요성을 설명하기 위한 장이다. 여기서 자원관리라 함은, CPU 자원을 뜻한다. 일반적으로 데몬을 작성할 때 유의해야 하는 사항은 바로 자원관리이다. Daemon이 CPU 자원과 메모리 자원을 90% 이상 소모한다면 그것이 어떻게 Daemon이라 하겠는가? 따라서 CPU를 최대한 작게 사용하면서 자신이 수행해야 하는 작업은 자주 수행하는 것이 좋다.

Daemon이 이런 방식으로 작동하기 위해서는 다음과 같은 원칙을 지켜주어야 한다.

  • Loop를 수행할 때는 반드시 time.sleep()를 사용하여 Step 마다 CPU를 양도할 것
  • 프로그램에서 사용하는 모든 모듈에 위 조항을 적용할 것.

 

설령 이렇게 한다 하더라도 크게 자원을 소모할 수 있다. 이 때는 스스로 관찰하여 최대한 자원소모를 줄이는 방법 밖에 없다. 자원 소모를 줄이는 방법은 대부분 프로그래머의 지식에 의존한다. 프로그래머의 지식이란, 크게 컴퓨터 구조에 관한 지식과 언어 자체에 대한 지식에 대한 것이 있다. 컴퓨터 구조에 관한 지식이란, 컴퓨터 구조에 대해서 잘 알고 거기에 최적화 된 코드를 작성하는 것을 말한다. 이를테면, CPU가 가지는 명령어에 대해서 잘 알고, 그 명령어가 최대한 작게 수행될 수 있는 형태를 말한다. 쉬운 예를 들자면 예전 CPU는 곱하기 연산을 덧셈을 수번 반복하여 수행하였다. 이 경우 CPU는 반복된 작업을 수행하게 된다. 하지만 쉬프트 연산을 사용할 경우 작업 횟수를 크게 줄일 수 있다. 이와 같이 컴퓨터 구조에 대한 지식을 가지고, 거기에 적합한 최적화 코드를 생성하는 것이다. 두 번째로 언어에 대한 지식이 있다. 현 시점에서 수없이 다양한 언어가 존재하며 이 언어는 다양한 형태로 제작되어 졌다. 때문에 언어에 대해서 정통하다면 CPU에서와 마찬가지로 연산 횟수를 크게 줄일 수 있다.

이와 같이 Resoruce를 줄이는 방법은 다양한 형태가 있으며 언어에 대한 지식에 크게 의존하며, 추가적으로 컴퓨터 구조에 관한 지식에 의존한다. 왜냐하면 컴퓨터 구조에 관한 지식보다는 언어에 대한 지식이 더 습득하기 쉬우며(여기에는 개인 차이가 있다) 최적화 하기가 더 용이하다.

 

Python Popen

Python에서 프로세스를 수행하는 방법은 다양한 방법이 있다. popen 함수와 system 함수를 이용하는 방법이 대표적이다. popen 함수는 입출력을 제어 여부에 따라서 popen2, popen3과 같은 함수로 다시 구분된다. 사실 이러한 구분 방법은 Python의 역사를 대변한다고 볼 수 있다. Python은 초기에는 OO 지향적 언어가 아니었으므로 함수이름을 이용하여 구분하였다. 하지만 이런 방법은 프로그래머로 인하여 함수 이름을 암기하는 고통을 증대 시킨다고 볼 수 있다. 따라서 최근 Python은 함수 이름 암기에 대한 부담을 덜고, 인자(Parameter)를 조절하여 역활을 구분하는 형태의 OO 지향적 모습을 많이 볼 수 있다. 이는 Ruby와 같은 후발주자에 대한 견제로 볼 수 있다. 한 개발자는 Python과 Ruby가 둘 다 뛰어난 언어임에는 틀림이 없으나, Python이 실행하는 다양한 형태의 함수와 그 다양한 함수를 사용하기 위해서 이름을 암기해야 하는 고통을 예로 들며 Ruby를 선호한다고 이야기 하였다. 이와 같이 함수 이름에 대한 암기 부담은 크게 작용하며 이를 줄이기 위한 노력은 Overloading와 같은 형태로 꾸준히 진화하였다.

Popen은 이러한 최근 Python에 대한 모습을 대변한다. Popen 클래스 하나를 이용하여 subprocess를 쉽게 수행하고 통신(Messaging)할 수 있다. Python Library Reference에서는 이와 같이 Popen을 사용하여 기존의 popen 함수와 system 함수를 대체하는 방법에 대해서 따로 서술한 문서가 있으며 본 절에서는 이 문서에서 서술하는 방법과 새롭게 변화된 프로세스 다루기에 대해서 설명한다.

 

기존 방법 Python 2.3 이하 (os.system)

          sts = os.system("mycmd" + " myarg")
          

새로운 방법 Python 2.4 이상 (Popen class)

          p = Popen("mycmd" + " myarg", shell=True)
          sts = os.waitpid(p.pid, 0)
          
          try:
              retcode = call("mycmd" + " myarg", shell=True)
              if retcode < 0:
                  print >>sys.stderr, "Child was terminated by signal", -retcode
              else:
                  print >>sys.stderr, "Child returned", retcode
          except OSError, e:
              print >>sys.stderr, "Execution failed:", e
          
           
          

코드 자체는 기존 코드가 조금 더 깔끔하다. 하지만 Popen 클래스를 이용하면 기존에 사용하였던 os.system(), popen(), popen2()와 같은 모든 함수를 Popen 클래스를 사용하여 대체 할 수 있다. 아래 예제는 popen 함수에 대한 예제이다.

 

기존방법 Python 2.3 이하 (os.popen)

          pipe = os.popen(cmd, mode='r', bufsize)
          

새로운 방법 Python 2.4 이상 (Popen Class)

          pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
          

이와 같이 Popen 클래스를 사용하면 기존 프로세스 관련 함수를 모두 대체할 수 있다. Popen 클래스는 subprecess 모듈에 포함되어 있으며 2.4 부터 지원한다.

Reference#

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012

http://www.thescripts.com/forum/thread605748.html

http://www.joinc.co.kr/modules/moniwiki/wiki.php/man/2/setsidman/2/setsid

http://bbs.python.or.kr/viewtopic.php?t=22300&amp;highlight=%EB%8D%B0%EB%AA%ACAA%AC

 

Cron Table Reference#

http://blog.naver.com/lukers?Redirect=Log&amp;logNo=70020680512



http://www.py2exe.org 에서 프로그램을 다운받는다. 

설명서대로 한다. 아주 쉽다. py2exe를 설치한 후 python/lib/site-package/py2exe/example 폴더에 보면 예제가 더 많다. 이 파일의 실행은 다음과 같이 한다.

python setup.py py2exe

빌드 중에 unicode 에러가 나오면 다음과 같이 한다.

  • C:\Python24\Lib\site.py 를 연다.
  • encoding = "ascii"를 찾는다 (기본 설정임)
  • 여기에 #를 달아 주석처리하고, 다음과 같이 넣고 저장한 후 다시 실행해본다. encoding = "euc-kr" 이래도 안되면 "utf-8" 그래도 안되면 "uhc"
  • 컴파일이 된다. ^^


map과 multimap 데이터 타입이 제공하는 멤버 함수에 관해 자세히 살펴본다. 멤버 함수들이 기초적인 연산들을 제공하는 반면에, 13절과 14절에 설명하고 있는 generic 알고리듬을 사용함으로써 데이터 구조를 보다 유용하게 사용할 수 있게 된다. 

9.2.1 map의 선언과 초기화

map의 선언은 표준 라이브러리에서 자주 보아왔던 방식을 따른다. map은 템플릿 데이터 구조이며, 템플릿의 인자로 키의 타입, 키에 대응되는 값의 타입, 키를 비교하는데 사용될 비교 연산자를 지정해야 한다. 만약에 컴파일러가 디폴트 템플릿 인자(아직은 많은 회사에서 지원하지 않는 C++의 새특징)를 지원하면, 방금 언급한 템플릿 인자 중 세번째 것은 생략이 가능하고, 생략되는 경우에는 키의 타입에 정의되어 있는 less-than 연산자로 지정된다. map은 초기값없이 선언될 수 있고, 한쌍의 반복자를 사용하여 다른 컨테이너에 담긴 값들로 초기화 될 수도 있다. 후자의 경우에, 반복자는 pair 타입의 값을 가리키고 있어야 하며, 이때 첫번째 필드는 키(key)로 간주되고, 두번째 필드는 값(value)으로 간주되는 것이다. 복사 생성자를 사용하여 다른 map의 복사본을 만들어 낸다.
   // map indexed by doubles containing strings
   map<double, string, less<double> > map_one;
   // map indexed by integers, containing integers   
   map<int, int> map_two(aContainer.begin(), aContainer.end());
   // create a new map, initializing it from map two
   map<int, int> map_three(map_two);   // copy constructor
map을 다른 map에 대입할 수 있으며, swap() 연산을 사용하여 두 map간에 값을 서로 교환할 수 있다. 

9.2.2 타입 정의

map과 multimap은 많은 타입 정의들을 포함하고 있으며, 이들은 주로 선언문에서 많이 사용된다. 예를 들어, string을 정수로 매핑하는 map에 사용되는 반복자는 다음과 같이 선언된다.
    map<string, int>::iterator location;
'iterator' 뿐만 아니라, 다음과 같은 타입들이 정의되어 있다. 
 
key_type map을 인덱싱할 때 사용하는 키의 타입
value_type 맵이 담고 있는 데이터 즉 key/value pair 타입
const_iterator 자신이 가리키는 원소를 변경할 수 없는 상수 반복자
reverse_iterator 역방향으로 이동하는 역반복자
const_reverse_iterator 상수 반복자와 역 반복자의 성질을 모두 가지는 반복자
reference A reference to an underlying value.
const_reference A reference to an underlying value that will not permit the element to be modified.
size_type An unsigned integer type, used to refer to the size of containers.
key_compare A function object that can be used to compare two keys.
value_compare A function object that can be used to compare two elements.
difference_type A signed integer type, used to describe the distances between iterators.
allocator_type An allocator used by the container for all storage management.

9.2.3 삽입과 접근

insert() 연산을 사용하여 map이나 multimap에 값들을 삽입한다. 이때 인자는 반드시 키와 값의 pair이어야 한다. 이때 사용되는 pair는 map에 정의되어 있는 value_type이라는 데이터 타입을 사용하여 만든다.

   map_three.insert(map<int>::value_type(5, 7));
한쌍의 반복자를 사용하여 삽입을 수행할 수도 있는데, 다음과 같이 다른 컨테이너로부터 값을 가져올 수 있다.
   map_two.insert(map_three.begin(), map_three.end());

map(multimap은 제외)에서는 첨자 연산을 통해 값을 접근하거나 삽입할 수 있다. 단순히 키를 첨자로 사용하면 해당 값을 접근할 수 있고, 첨자 연산의 결과에 대입을 함으로써 키에 대응되는 값을 변화시킬 수 있다.

   cout << "Index value 7 is " << map_three[7] << endl;
      // now change the associated value
   map_three[7] = 5;
   cout << "Index value 7 is " << map_three[7] << endl;

9.2.4 삭제

키값을 가지고 map과 multimap으로부터 값들을 삭제할 수 있다. multimap에서는 키와 연관된 모든 원소를 삭제한다. 삭제될 원소를 반복자로 지정할 수도 있다. 예를 들면, find() 연산의 결과로 얻은 반복자를 사용할 수도 있다. 한쌍의 반복자를 사용하여 지정한 range내의 원소들을 모두 지우는 것도 가능하다.

   // erase the 4th element 4
   map_three.erase(4);
   // erase the 5th element 
   mtesttype::iterator five = map_three.find(5);
   map_three.erase(five);
   
   // erase all values between the 7th and 11th elements
   mtesttype::iterator seven = map_three.find(7);
   mtesttype::iterator eleven = map_three.find(11);
   map_three.erase(seven, eleven);
원소타입이 소멸자를 정의하고 있다면, 콜렉션으로부터 키와 값을 제거하기 전에 이 소멸자가 호출될 것이다. 

9.2.5 반복자

 No Iterator Invalidation 

begin() end() 멤버 함수는 map과 multimap의 경우 양방향 반복자를 생성한다. 이렇듯, map과 multimap에서 사용되는 반복자들은 양방향 반복자이므로 대소 비교(<, >, <=, >=)가 허용되지 않는다는 점에 유의하자(2.2절의 표를 참고). map이나 multimap에 사용되는 반복자를 참조하면 키(key)와 값(value)의 pair를 얻게 된다. 따라서, 필드명으로 first를 사용하면 키를, second를 사용하면 값(value)을 얻을 수 있다. 첫번째 필드는 상수라서 변경할 수 없지만, 두번째 필드는 주어진 키(key)와 연결된 값(value)을 변화시키는데 사용된다. 원소들은 키 필드의 순서에 따라 접근된다(?). 

rbegin() rend() 멤버 함수는 역방향으로 원소들을 생성하는 반복자들을 반환한다. 

9.2.6 검색과 카운팅

size() 멤버 함수는 컨테이너가 가지고 있는 원소의 갯수를 리턴한다. empty() 멤버 함수는 컨테이너가 비었을 때, true를 리턴하고, size()를 zero와 비교하는 것보다 대체로 빠르다. 

find() 멤버함수는 키를 인자로 취해서, 키/값 pair를 가리키는 반복자를 리턴한다. multimap의 경우에는, 가장 먼저 일치하는 값을 리턴한다. 두경우 모두, 원하는 값을 찾지 못할 때는, past-the-end 반복자를 리턴한다.

   if (map_one.find(4) != map_one.end())
      cout << "contains a 4th element" << endl;

lower_bound() 멤버 함수는 인자로 주어진 키와 일치하는 첫번째 원소를 리턴하고, upper_bound() 멤버 함수는 인자로 주어진 키와 일치하는 마지막 원소 바로 직후의 원소를 리턴한다. 마지막으로,equal_range() 함수는 lower bound와 upper bound를 담고 있는 반복자의 pair를 리턴한다. 이들 연산을 사용하는 예는 이 절의 마지막에 주어진다. 

count() 멤버 함수는 인자로 주어진 키값과 일치하는 원소의 갯수를 리턴한다. map의 경우에는 결과값이 항상 0 또는 1인 반면에, multimap의 경우에는 음수가 아닌 값이면 결과값이 될 수 있다. 단순히 콜렉션이 주어진 키로 인덱싱되는 원소를 포함하는지의 여부만을 확인하고 싶을 때는 find()의 결과값을 end-of-sequence 반복자와 비교하는 것보다는 count()를 사용하는 것이 쉬울 때가 더 많다.

   if (map_one.count(4))
      cout << "contains a 4th element" << endl;

9.2.7 원소 비교

인자가 필요없는 key_comp() value_comp() 멤버함수는 각각 key 타입 또는 value 타입의 원소를 비교할 때 사용되는 함수 객체를 반환한다. 비교할때 사용되는 값들이 콜렉션이 포함되어 있을 필요는 없으며, 이 함수들은 컨테이너에 아무런 영향을 미치지 않는다.

    if (map_two.key_comp (i, j)) // map_two.key_comp()(i, j) ?
       cout << "element i is less than j" << endl;

9.2.8 기타 map 연산

map과 multimap은 ordered 콜렉션이고, map에 대한 반복자는 pair를 반환하기 때문에, 13절과 14절에서 설명하는 함수들이 사용하기에 무의하거나 곤란한 것들이 많다. 그러나, for_each(), adjacent_find(),accumulate()들 각각은 나름대로 쓸모가 있다. 하지만, 인자로 제공되는 함수들은 키/값 pair를 인자로 취하는 것이라야 한다는 것을 반드시 기억해야 한다. 



[출처 : http://web.lge.cn:8000/system/2232]

pair를 사용하면 두개값 (혹은 사용자정의시 두개이상의 값)을 리턴받을수있다.
컨테이너클래스인 map과 multimap 은 key/value 쌍이라고 불리는 원소를 다루기위해 사용한다.
make_pair(T, T)는 묵시적타입을 제공하지만 부동소수점의 경우에는 double로 변환한다.

이하 예제는 리턴함수와 동적공간생성 , 정적공간생성을 각각수행하고 있다.

#include "stdafx.h"

#include <iostream>
#include <utility>
using namespace std;


pair<int, const char*> OutPut()
{
 return make_pair(20, "hello return");
}

int _tmain(int argc, _TCHAR* argv[])
{
 pair<int, int> * a = new pair<int, int>(1,1);
 cout << a->first << endl;

 pair<int, const char*> b = make_pair(10,"hello");
 cout << b.second << endl;

 cout << OutPut().first << endl;
 cout << OutPut().second;

 delete a;
 return 0;

}




[출처 : http://rickd.egloos.com/4892907]
제공 : 한빛 네트워크
저자 : 최흥배
이전기사 : 이번에는 이전 회에 설명한 hash_map과 같은 연관 컨테이너 중의 하나인 map에 대해서 설명합니다. 사용법이 hash_map과 대부분 비슷해서 앞으로 할 이야기가 별로 어렵지 않을 것입니다. 

이전 회에서 설명했던 것은 가급적 또 설명하지 않을 테니 앞의 글들을 보지 않은 분들은 꼭 봐 주세요. 그럼 map에 대한 이야기를 시작 하겠습니다. 

7.1 map의 자료구조 

map의 자료구조는 '트리(tree)'입니다(정확하게 말하면 트리 자료구조 중의 하나인 '레드-블랙 트리(Red-Black tree)'입니다). 

트리는 한글로 '나무'입니다. 나무는 뿌리에서 시작하여 여러 갈래의 가지가 있고, 가지의 끝에는 나무 잎이 있습니다. 트리 자료구조도 이와 같은 형태를 가지고 있어서 루트(root), 리프(leaf)라는 용어를 사용합니다. 

그림1 
[그림 1] 실제 나무(왼쪽)와 트리 자료구조의 모습(오른쪽) 

오른쪽의 트리 자료구조에서 제일 최상 위의 '5'는 루트 노드(root node)라고 하며, 노드'5'와 노드'7'의 관계에서 노드'5'는 부모 노드(parent node), 노드'7'은 자식 노드(child node)라고 합니다. 또한 노드 '12'와 노드 '30'의 관계에서는 부모 노드는 노드'12'입니다. 자식이 없는 노드는 리프 노드(leaf node)라고 합니다. 그림 1에서는 '9', '30', '35', '20'이 리프 노드입니다. 

7.2 트리 자료구조의 특징 

트리는 노드를 균형 있게 가지는 것이 성능에 유리하기 때문에 기본 트리에서 변형된 B-트리, B+ 트리, R-트리, 레드 블랙 트리, AVL 트리 등 다양한 종류의 트리 자료구조가 있습니다. 

균형을 이룬 트리는 자료를 정해진 방식에 따라서 분류하여 저장하기 때문에 시퀸스(일렬로)하게 자료를 저장하는 연결 리스트에 비해서 검색이 빠릅니다. 그렇지만 정해진 규칙에 따라서 자료를 삽입, 삭제 해야 되기 때문에 삽입과 삭제가 간단하지 않으며 구현이 복잡합니다. 

7.3 map을 언제 사용해야 될까? 

map은 많은 자료를 정렬하여 저장하고 있고 빠른 검색을 필요로 할 때 자주 사용합니다. 많은 자료를 빠르게 검색한다고 하는 부분은 앞 회에서 설명한 hash_map과 비슷합니다. 그러나 hash_map과 크게 다른 부분이 있습니다. map은 자료를 저장할 때 내부에서 자동으로 정렬을 하고, hash_map은 정렬하지 않는다라는 것입니다. 정렬이 필요하지 않는 곳에서 map을 사용하는 것은 불 필요한 낭비입니다. 

map은 아래 조건일 때 사용하면 좋습니다. 

1. 정렬해야 한다.
2. 많은 자료를 저장하고, 검색이 빨라야 한다
3. 빈번하게 삽입, 삭제하지 않는다. 

7.4 map 사용 방법 

가장 먼저 map의 헤더파일을 포함합니다.
#include 
보통 map을 사용하는 방법은 아래와 같습니다.
map< key 자료 type, value 자료 type > 변수 이름

map< int, int > map1;
value는 저장할 자료이고, key는 value를 가리키는 것입니다. 위에서는 key의 자료형 int, value 자료형 int인 map을 생성합니다. 

앞에서 map은 자료를 저장할 때 정렬을 한다고 말했습니다. 정렬의 대상은 key를 대상으로 하며오름차순으로 정렬합니다. 그래서 내림차순으로 정렬하고 싶거나 key의 자료형이 기본형이 아닌 유저 정의형(class나 struct로 정의한 것)인 경우는 정렬 방법을 제공해야 합니다. 

위에 생성한 map1은 오름차순으로 정렬하는데 이것을 내림차순으로 정렬하고 싶다면 아래와 같이 하면 됩니다.
map< key 자료 type, value 자료 type, 비교 함수 > 변수 이름

map< int, int, greater<int> > map1;
위에서 사용한 비교 함수 greater는 제가 따로 만든 것이 아니고 STL에 있는 템플릿입니다. 

greater와 같은 것을 STL 알고리즘 이라고 하는데 이것들은 다음 시간에 자세하게 설명할 예정이니 여기서는 이런 것이 있다는 것만 아시면 됩니다. 

다른 컨테이너와 같이 map도 동적 할당을 할 수 있습니다. 사용 방법은 앞서 소개한 컨테이너들과 비슷합니다. 

앞 회의 hash_map과 비교를 하면 사용 방법이 거의 같다라는 것을 알 수 있습니다. 이후 소개하는 map의 멤버함수도 일부분만 제외하고는 hash_map과 같습니다. 이전에도 이야기 했지만 서로 다른 컨테이너가 사용방법이 서로 비슷하여 하나만 제대로 배우 나머지 것들도 배우기 쉽다라는 것이 STL의 장점 중의 하나입니다. 

7.4.1 map의 주요 멤버들 

멤버 설명
begin 첫 번째 원소의 랜덤 접근 반복자를 반환
clear 저장하고 있는 모든 원소를 삭제
empty 저장 하고 있는 요소가 없으면 true 반환
End 마지막 원소 다음의(미 사용 영역) 반복자를 반환
erase 특정 위치의 원소나 지정 범위의 원소들을 삭제
Find key와 연관된 원소의 반복자 반환
insert 원소 추가
lower_bound 지정한 key의 요소를 가지고 있다면 해당 위치의 반복자를 반환
operator[] 지정한 key 값으로 원소 추가 및 접근
rbegin 역방향으로 첫 번째 원소의 반복자를 반환
rend 역방향으로 마지막 원소 다음의 반복자를 반환
size 원소의 개수를 반환
upper_bound 지정한 key 요소를 가지고 있다면 해당 위치 다음 위치의 반복자 반환

[표 1] map의 주요 멤버들 

7.4.2. 추가 

map 에서는 자료를 추가 할 때 insert를 사용합니다.
원형 : 
pair <iterator, bool> insert( const value_type& _Val );
iterator insert( iterator _Where, const value_type& _Val );
template<class InputIterator> void insert( InputIterator _First, InputIterator _Last );
첫 번째 방식이 보통 가장 자주 사용하는 방식입니다.
map< int, int > map1;

// key는 1, value는 35를 추가.
map1.insert( map< int, int >::value_type(1, 35));

// 또는 STL의 pair를 사용하기도 합니다.
typedef pair < int, int > Itn_Pair;
map1.insert(  Int_Pair(2, 45) );
두 번째 방식으로는 특정 위치에 추가할 수 있습니다.
// 첫 번째 위치에 key 1, value 35를 추가
map1.insert( map1.begin(), map< int, int >::value_type(1, 35) );

// 또는
map1.insert( map1.begin(),  Int_Pair(2, 45) );
세 번째 방식으로는 지정한 반복자 구간에 있는 것들을 추가합니다.
map< int, int > map2;
// map1의 모든 요소를 map2에 추가.
map2.insert( map1.begin(), map1.end() );
map은 이미 있는 key 값을 추가할 수 없습니다(복수의 key 값을 사용하기 위해서는 multi_map을 사용해야 합니다). 가장 자주 사용하는 첫 번째 방식으로 추가하는 경우는 아래와 같은 방법으로 결과를 알 수 있습니다.
pair< map<int, int>::iterator, bool > Result;
Result = map1.insert( Int_Pair(1, 35));
만약 이미 key 값 1이 추가 되어 있었다면 insert 실패로 Result.second 는 false이며, 반대로 성공하였다면 true 입니다. 

operator[]를 사용하여 추가하기 

insert가 아닌 operator[]를 사용하여 추가할 수도 있습니다.
// key 10, value 80을 추가
map1[10] = 80;
7.4.3. 반복자 사용 

다른 컨테이너와 같이 정 방향 반복자 begin(), end()와 역 방향 반복자 rbegin(), rend()를 지원합니다. 

사용 방법은 다음과 같습니다.
// 정 방향으로 map1의 모든 요소의 value 출력
map< int, int >::iterator Iter_Pos;
for( Iter_Pos = map1.begin(); Iter_Pos != map1.end(); ++Iter_Pos)
{
   cout << Iter_Pos.second << endl;
}

// 역 방향으로 map1의 모든 요소의 value 출력
map< int, int >::reverse_iterator Iter_rPos;
for( Iter_rPos = map1.rbegin(); Iter_rPos != map1.rend(); ++Iter_rPos)
{
   cout << Iter_rPos.second << endl;
}
위에서 map을 정의할 때 비교함수를 사용할 수 있다고 했습니다. 만약 비교함수를 사용한 경우는 반복자를 정의할 때도 같은 비교함수를 사용해야 합니다.
map< int, int, greater<int> > map1;
map< int, int, greater<int> >::iterator Iter_Pos;
7.4.4. 검색 

map에서 검색은 key 값을 대상으로 합니다. key와 같은 요소를 찾으면 그 요소의 반복자를 반환하고, 찾지 못한 경우에는 end()를 가리키는 반복자를 반환합니다.
원형 : 
iterator find( const Key& _Key );
const_iterator find( const Key& _Key ) const;
두 방식의 차이는 반환된 반복자가 const냐 아니냐는 차이입니다. 첫 번째 방식은 const가 아니므로 찾은 요소의 value를 변경할 수 있습니다(참고로 절대 key는 변경 불가입니다). 그러나 두 번째 방식은 value를 변경할 수 없습니다.
// key가 10인 요소 찾기.
map< int, int >::Iterator FindIter = map1.find( 10 );

// 찾았다면 value를 1000으로 변경
if( FindIter != map1.end() )
{
   FindIter->second = 1000;
}
7.4.5. 삭제 

저장하고 있는 요소를 삭제할 때는 erase와 clear를 사용합니다. erase는 특정 요소를 삭제할 때 사용하고, clear는 모든 요소를 삭제할 때 사용합니다. 

erase
원형 : 
iterator erase( iterator _Where );
iterator erase( iterator _First, iterator _Last );
size_type erase( const key_type& _Key );
첫 번째 방식은 특정 위치에 있는 요소를 삭제합니다.
// 두 번째 위치의 요소 삭제.
map1.erase( ++map1.begin() );
두 번째 방식은 지정한 구역에 있는 요소들을 삭제합니다.
// map1의 처음과 마지막에 있는 모든 요소 삭제
map1.erase( map1.begin(), map1.end() );
세 번째 방식은 지정한 키와 같은 요소를 삭제합니다.
// key가 10인 요소 삭제.
map1.erase( 10 );
첫 번째와 두 번째 방식에서는 삭제하는 요소의 다음을 가리키는 반복자를 반환하고(C++ 표준에서는 반환하지 않습니다만 Microsoft의 Visual C++에서는 반환합니다), 세 번째 방식은 삭제된 개수를 반환합니다. map에서는 세 번째 방식으로 삭제를 하는 경우 정말 삭제가 되었다면 무조건 1이지만, multi_map에서는 삭제한 개수만큼의 숫자가 나옵니다. 

clear 

map의 모든 요소를 삭제할 때는 clear를 사용합니다.
map1.clear();
이것으로 map에서 자주 사용하는 멤버들에 대한 설명은 끝났습니다. [표 1]에 나와 있는 멤버들 중 사용 방법이 간단한 것은 따로 설명하지 않으니 [리스트 1]의 코드를 봐 주세요. 

[리스트 1] 정렬된 아이템 리스트 출력
#include <map>
#include <string>
#include <iostream>

using namespace std;

struct Item
{
  char Name[32];  // 이름
  char Kind;  // 종류
  int BuyMoney;  // 구입 가격
  int SkillCd;  // 스킬 코드
};

int main()
{
  map< char*, Item > Items;
  map< char*, Item >::iterator IterPos;
  typedef pair< char*, Item > ItemPair;

  Item Item1;
  strncpy( Item1.Name, "긴칼", 32 );
  Item1.Kind = 1;    Item1.BuyMoney = 200;  Item1.SkillCd = 0;

  Item Item2;
  strncpy( Item2.Name, "성스러운 방패", 32 );
  Item2.Kind = 2;    Item2.BuyMoney = 1000;  Item2.SkillCd = 4;

  Item Item3;
  strncpy( Item3.Name, "해머", 32 );
  Item3.Kind = 1;    Item3.BuyMoney = 500;  Item3.SkillCd = 0;

  // Items에 아이템 추가
  Items.insert( map< char*, Item >::value_type(Item2.Name, Item2) );
  Items.insert( ItemPair(Item1.Name, Item1) );

  // Items가 비어 있지않다면
  if( false == Items.empty() )
  {
    cout << "저장된 아이템 개수- " << Items.size() << endl;
  }

  for( IterPos = Items.begin(); IterPos != Items.end(); ++IterPos )
  {
    cout << "이름: " << IterPos->first << ", 가격: " << IterPos->second.BuyMoney << endl;
  }

  IterPos = Items.find("긴칼");
  if( IterPos == Items.end() )   {
    cout << "아이템'긴칼'이 없습니다." << endl;
  }
  cout << endl;

  cout << "올림차순으로 정렬되어있는 map(Key 자료형으로string 사용)" << endl;
  
  map< string, Item, less<string> > Items2;
  map< string, Item, less<string> >::iterator IterPos2;
  
  Items2.insert( map< string, Item >::value_type(Item2.Name, Item2) );
  Items2.insert( ItemPair(Item1.Name, Item1) );
  // operator[]를 사용하여 저장
  Items2[Item3.Name] = Item3;

   for( IterPos2 = Items2.begin(); IterPos2 != Items2.end(); ++IterPos2 )
  {
    cout << "이름: " << IterPos2->first << ", 가격: " << IterPos2->second.BuyMoney << endl;
  }
  cout << endl;

  cout << "해머의 가격은 얼마? ";
  IterPos2 = Items2.find("해머");
  if( IterPos2 != Items2.end() )   {
    cout << IterPos2->second.BuyMoney << endl;
  }
  else {
    cout << "해머는 없습니다" << endl;
  }
  cout << endl;

  // 아이템 "긴칼"을 삭제한다.
  IterPos2 = Items2.find("긴칼");
  if( IterPos2 != Items2.end() )   {
    Items2.erase( IterPos2 );
  }

  cout << "Items2에 있는 아이템 개수: " << Items2.size() << endl;

  return 0;
}
결과 

그림2 

[리스트 1]의 Items에서 '긴칼'을 검색을 하면 찾을 수가 없습니다. 이유는 key의 자료형으로 char*을 사용했기 때문입니다. 그래서 Items2에서는 STL의 문자열 라이브러리인 string을 사용하였습니다. String에 대해서는 다음 기회에 설명할 예정이니 문자열을 처리하는 라이브러리라고 알고 계시면 됩니다. 

이것으로 map에 대한 설명이 끝났습니다. 이전 회의 hash_map과 비슷한 부분이 많아서 hash_map에 대한 글을 보셨던 분들은 쉽게 따라왔으리라 생각합니다. 그리고 map과 hash_map에 대하여 잘못 알고 있어서 정렬이 필요하지 않은 곳에서 map을 사용하는 경우가 있는데 조심하시기 바랍니다. 제가 미쳐 설명하지 않은 부분에 대해서는 MSDN에 있는 map 설명을 참조하시기를 바랍니다(http://msdn.microsoft.com/ko-kr/library/xdayte4c.aspx).



[출처 : http://www.hanb.co.kr/network/view.html?bi_id=1618]

1.5버전이후인가?? 아무튼 언제부터인가 이클립스의 느낌표를 클릭하면

자동 생성되는 Generic이란 녀석 :)

그냥 그렇게 생각했었는데, 생각외로 여러모로 쓸모가 많았다.

C++의 템플릿의 비스무리 한거 같기도 하지만서도ㅋㅋ

 

늘 자료구조때 골머리를 앓게 하는건, 검색과 정렬이다.

아직도 수많은 컴퓨터공학도들이

자료구조란 학문을 하며 골머리를 썩고 있는데ㅋㅋ

 

그만큼 자료구조라는 학문이 중요하기 때문이고

모든 컴퓨터공학의 근간이 되는 학문임은 강조에 강조를 해도

부족하지 않다.

 

자바를 쓰다보면 느꼈던건 역시 막강하고 손쉽게 사용할 수 있는 API에 있다.

이번에도 항상 나만의 정렬, 검색 알고리즘을 구현(?)해서 사용하다가,

이번에 Collections를 이용해봤는데, 여간 편리한게 아니다-_ -);

역시 난 시대에 뒤떨어지고 있었던 것인가??;;

 

그래서 잠깐 소개하고자 한다.

 

먼저 알아야 할것은 바로 이 두 interface다.

 

java.lang Interface Comparable<T>

java.util Interface Comparator<T>

 

저 두 인터페이스를 이용해서 Collections.sort를 이용해 정렬을 할 수가 있는데,

List에 들어가는 객체에 Comparable을 implements함으로써, 정렬을 할 수있게 된다.

 

방법1. Comparable을 implements한다.

 

class PersonalInfo implements Comparable<PersonalInfo>{
 
 private String name = null;
 
 private String phoneNumber = null;
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
   this.name = name;
 }
 
 public String getPhoneNumber() {
  return phoneNumber;
 }
 
 public void setPhoneNumber(String phoneNumber) {
  this.phoneNumber = phoneNumber;
 }
 
  public boolean getSortingMode() {
  return sortingMode;
 }

 public void setSortingMode(boolean sortingMode) {
  this.sortingMode = sortingMode;
 }
 
 
@Override
 public int compareTo(PersonalInfo o) {
    return name.compareTo( o.getName() );
  }

}

 

compareTo라는 메소드를 구현해야 하는데, 그 안에 비교하고자 하는 항목을 compareTo를 이용하여

비교해준 후,

Collections.sort()의 파라미터로 PersonalInfo객체가 들어있는 리스트를 넣어주기만 하면 된다.

 

위와 같은 방법은 편리하나, 여러가지 항목으로 정렬을 해야 할 경우 제약이 좀 있다.

 

그래서 사용하는 것이,

바로 Comparator 이다.

 

방법2. Comparator사용.

 

     Comparator<PersonalInfo> comparator = new Comparator<PersonalInfo>(){
      @Override
      public int compare(PersonalInfo o1, PersonalInfo o2) {      
       return o1.getName().compareTo( o2.getName() );
      }      
     };
     Collections.sort( list, comparator );

 

와 같은 방법으로 할 수 있다. 여러개의 정렬 항목에 따른 comparator를 만든 후, Collections의 파라미터로 던져주면,

정렬은 더이상 고민하지 않아도 될 것이다.

 

참고로 Collections.sort()의 documents내용은 다음과 같다.

public static <T extends Comparable<? super T>> void sort(List<T> list)

public static <T> void sort(List<T> list, Comparator<? super T> c)

 




[출처 : http://blog.naver.com/celestialorb/40065368727]

Java Collections Framework 의 일원이며 Collection 객체들을(set, list, map ..... ) 유용하게 사용하기위한 util class이다.

주로 연산작업이나 콜렉션객체를 리턴한다.

 

Field

  • EMPTY_LIST
  • EMPTY_MAP
  • EMPTY_SET

        모두 직렬화가 가능하며 사이즈가 0인 객체를 만들어 준다. 각각 emptyList(), emptyMap(), emptySet() 메소드와 동일한 기능을 수행 함. (null값이 들어간 객체가 아닌 말그대로의 빈객체이므로 NullPointException이 발생하지 않는다)

 

Method

  • sort  :  public static <T extens Comparable<? super T>> void sort(List<T> list)
                  public static <T> void sort(List<T> list, Comporator<? super T> c)
    리스트 객체를 정렬해주는 메소드 comparble을 implements한 클래스를 이용해 정렬 할 수도 있고, Comparator를 이용해 조건을 여러개 주어서 검색할 수도 있다.
    Comparable 이나 Comparator 응용 방법 링크
  • 01 import java.util.ArrayList;
    02 import java.util.Collections;
    03 import java.util.Comparator;
    04 
    05 import java.lang.Comparable;
    06 
    07 public class TestCollections {
    08     public static void main(String args[]) throws Exception {

    09         ArrayList<PersonalInfo> list = new ArrayList();
    10         list.add(new PersonalInfo("홍길동","11-11-11"));
    11         list.add(new PersonalInfo("김개똥","66-66-66"));
    12         list.add(new PersonalInfo("정지훈","66-66-66"));
    13         list.add(new PersonalInfo("정지훈","44-44-44"));
    14         list.add(new PersonalInfo("이소라","33-33-33"));
    15         list.add(new PersonalInfo("손예진","55-55-55"));
    16         System.out.println("====== 정렬전 ======");
    17         for(int idx = ; idx < list.size() ; idx++) {
    18           System.out.println(list.get(idx).getName() ", " 
    19                            + list.get(idx).getPhoneNumber());
    20         }
    21         Collections.sort(list);
    22         System.out.println("====== 정렬후 ======");
    23         for(int idx = ; idx < list.size() ; idx++) {
    24           System.out.println(list.get(idx).getName() ", " 
    25                            + list.get(idx).getPhoneNumber());
    26         }
    27         //@Override  여러검색조건을 선택할때 사용
    28         Comparator<PersonalInfo> comparator = 

    29           new Comparator<PersonalInfo>(){

    30           public int compare(PersonalInfo o1, PersonalInfo o2) {

    31            return o1.getName().compareToo2.getPhoneNumber() );
    32           }      
    33          };
    34         Collections.sortlist, comparator );
    35         System.out.println("====== 정렬후 ======");
    36         for(int idx = ; idx < list.size() ; idx++) {
    37           System.out.println(list.get(idx).getName() ", " 
    38                            + list.get(idx).getPhoneNumber());
    39         }
    40     }
    41 }
    42 //검색조건이 하나일 때 유용.

    43 class PersonalInfo implements Comparable<PersonalInfo>{
    44  
    45   private String name = null;
    46   private String phoneNumber = null;
    47   private boolean sortingMode = false;
    48   
    49   public PersonalInfo(String name, String phoneNumber) {
    50     this.name = name;
    51     this.phoneNumber = phoneNumber;
    52     this.sortingMode = true;
    53   }
    54  
    55   public void setName(String name) {   
    56     this.name = name; 
    57   }
    58   public void setPhoneNumber(String phoneNumber) {  
    59     this.phoneNumber = phoneNumber; 
    60   }
    61   public void setSortingMode(boolean sortingMode) {  
    62     this.sortingMode = sortingMode; 
    63   }
    64  
    65   public String  getName() {  return name; }
    66   public String  getPhoneNumber() {  return phoneNumber; }
    67   public boolean getSortingMode() {  return sortingMode; }
    68  
    69   //@Override
    70   public int compareTo(PersonalInfo o) {
    71     return name.compareToo.getName() );
    72   }
    73 
    74 }

     

    결과 값 :

    ====== 정렬전 ======
    홍길동, 11-11-11
    김개똥, 66-66-66
    정지훈, 66-66-66
    정지훈, 44-44-44
    이소라, 33-33-33
    손예진, 55-55-55
    ====== 정렬후 ======
    김개똥, 66-66-66
    손예진, 55-55-55
    이소라, 33-33-33
    정지훈, 66-66-66
    정지훈, 44-44-44
    홍길동, 11-11-11
    ====== 정렬후 ======
    홍길동, 11-11-11
    정지훈, 44-44-44
    정지훈, 66-66-66
    이소라, 33-33-33
    손예진, 55-55-55
    김개똥, 66-66-66


  • binarySearch :
    public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
    public static <T> int binarySearch(List<? extends T> list, T key,  Comparator<? super T> c)
    메소드 수행전 반드시 정렬되어있어야 하며, 정렬되지 않았을 때 수행시 결과값은 undefined 된다. 검색 결과가 여러개일 경우 랜덤하게 추출하기 때문에 원하는 값을 보장하지 않는다. 결과값이 없을 경우 : (-(insertion point) - 1).
  • reverse : pubic static void reverse(List<?> list)
    리스트를 역순으로 정렬시킨다. (순차정렬이 아님)
  • suffle : public static void shuffle(List<?> list)
                    public static void shuffle(List<?> list,  Random rnd)
    랜덤하게 리스트를 섞는다.
  • swqp : public static void swap(List<?> list, int i, int j)
    i와 j 포지션의 위치를 서로 바꾼다. IndexOutOfException에 주의할 것
  • fill : public static <T> void fill(List<? super T> list, T obj)
    obj 값으로 모든 엘리먼트들을 변경한다
  • copy : public static <T> void copy(List<? super T> dest, List<? extends T> src )
    src리스트의 모든 엘리먼트들을 dest 리스트에 카피한다. 반드시 dest 리스트가 src리스트보다 커야하며, 아닐 경우 IndexOutOfException이 발생한다.
  • min :
    public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll )
    public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll, Comparator <? Super T>comp )
    최소값을 구한다. 자동소팅된다. 결과값이 여러개일 경우 Comparator 를이용해 처리한다.
  • max :
    public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll )
    public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll, Comparator <? Super T>comp )
    최대값을 구한다. 자동소팅된다. 결과값이 여러개일 경우 Comparator 를이용해 처리한다.
  • rotate : public static void rotate(List<?> list,  int distance)
    distance만큼 옆으로 이동한다. 양수일경우 오른쪽, 음수일경우 왼쪽으로 이동 된다. list.size()보다 클 순 없다.
    보통 subList를 이용해 많이 활용된다. Collections.rotate( list.subList(j, k+1), distance)
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 의 엘리먼트를 갖는 리스트의 경우
    Collections.rotate( list.subList(2,5), 1) ); 수행 하면
    [1, 2, 5, 3, 4, 6, 7, 8, 9, 10] 로변경된다.
  • replaceAll : public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
    list에서 oldVal값을 찾아서 newVal값으로 변경 (oldVal == null ? e == null : oldVal.equals(e))
  • indexOfSubList : public static int indexOfSubList(List<?> source, List<?> target)
    source리스트에서 target리스트를 검색. source리스트에서 검색되면 가장 첫번째인덱스(lowest index )를 리턴한다. 검색이 되지 않을 경우 -1을 리턴한다.
  • lastIndexOfSubList : public static int lastIndexOfSubList(List<?> source, List<?> target)
    source리스트에서 target리스트를 검색. source리스트에서 검색되면 가장 첫번째인덱스(highest index )를 리턴한다. 검색이 되지 않을 경우 -1을 리턴한다.
  • unmodifiableCollection : public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c)
    읽기전용의 뷰컬렉션을 리턴한다. 컬렉션의 내용을 변경하게 되면 java.lang.UnsupportedOperationException 이 발생함. 각특성은  document확인
    동일기능의 메소드 -
    unmodifiableSet : public static <T> Set<T> unmodifiableSet(Set<? extends T> s )
    unmodifiableSortedSet : public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<T> s )
    unmodifiableList : public static <T> List<T> unmodifiableList(List<? extends T> list )
    unmodifiableMap : public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K,? extends V> m)
    unmodifiableSortedMap : public static <K,V> SortedMap<K,V> unmodifiableSortedMap(SortedMap<K,? extends V> m )
  • synchronizedCollection : public static <T> Collection<T> synchronizedCollection(Collection<T> c )
    컬렉션 동기화 . 동기화된 컬렉션을 리턴받아 Iterator를 사용하여, 리턴된 컬렉션을 반복처리하는 경우에는, 특별히, synchronized 블럭과 함께 사용해야 한다. 각 특성은 document 확인
    동일기능의 메소드 -
    synchronizedSet : public static <T> Set<T> synchronizedSet(Set<T> s )
    synchronizedSortedSet : public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s )
    synchronizedList : public static <T> List<T> synchronizedList(List<T> list )
    synchronizedMap : public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m )
    synchronizedSortedMap : public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m )




  •  

     





[출처 : http://blog.naver.com/ily1201/91140240]
pthread를 써야하는 이유는 단일 프로세서에서는 동시에 여러 작업을 하는 것처럼 유저에게 보여야하는 일이 있는데 간단한 예로 테트리스 게임같이 블록이 일정시간마다 자동적으로 내려가도록 하는 부분과 키 입력을 받을 때만 블록을 입력받은 방향으로 이동시키는 부분등... 이걸 멀티 쓰레드로 만들지 않는다면 아마 프로그래밍이 상당히 복잡해지고 구현하는데도 상당히 어려울 것이다. 이를 쉽게 처리하기위해서 pthread API 함수를 이용하여 쉽게 구현할 수 있다.


pthread API 함수를 쓰기 위해서 pthread.h 파일을 include 해야만 한다.
#include <pthread.h> 

그리고 필히 컴파일시 -lpthread 옵션 추가해주어야만 한다. 빠뜨리면 컴파일 오류가 일어나거나 아니면 쓰레드가 정상 작동하지 않을 것이다.

pthread_t : pthread의 자료형을 의미

int pthread_create( pthread_t *th_id, const pthread_attr_t *attr, void* 함수명, void *arg );
 - pthread 생성한다.
첫 번째 인자 : pthread 식별자로 thread가 성공적으로 생성되면 thread 식별값이 주어진다.
두 번째 인자 : pthread 속성(옵션), 기본적인 thread 속성을 사용할 경우 NULL
세 번째 인자 : pthread로 분기할 함수. 반환값이 void* 타입이고 매개변수도 void* 으로 선언된 함수만 가능하다. ex) void* handler (void* arg) { ... }
네 번째 인자 : 분기할 함수로 넘겨줄 인자값. 어떤 자료형을 넘겨줄 지 모르기 때문에 void형으로 넘겨주고 상황에 맞게 분기하는 함수내에서 원래의 자료형으로 캐스팅해서 사용하면 된다.
리턴 값 : 성공적으로 pthread가 생성될 경우 0 반환

int pthread_join( pthread_t th_id, void** thread_return );
 - 특정 pthread가 종료될 때까지 기다리다가 특정 pthread가 종료시 자원 해제시켜준다.
첫 번째 인자 : 어떤 pthread를 기다릴 지 정하는 식별자
두 번째 인자 : pthread의 return 값, 포인트로 값을 받아오는 점을 주의할 것.

int pthread_detach( pthread_t th_id );
 - th_id 식별자를 가지는 pthread가 부모 pthread로부터 독립한다. 즉 이렇게 독립된 pthread는 따로 pthread_join()이 없어도 종료시 자동으로 리소스 해제된다.

void pthread_exit( void* ret_value );
 - 현재 실행중인 thread를 종료시킬 대 사용한다. 보통 pthread_exit가 호출되면 cleanup handler가 호출되며 보통 리소스 해제하는 일을 수행한다.

void pthread_cleanup_push( void* (함수명), void* arg );
 - pthread_exit()가 호출될 때 호출된 handler를 정하는 함수.  보통 자원 해제용이나 mutex lock 해제를 위한 용도로 사용된다.

void pthread_cleanup_pop(int exec);
- 설정된 cleanup handler를 제거하기 위해서 사용되는 함수.  exec 값을 0일 경우 바로 cleanup handler 제거하고 그외의 값을 가질 경우 cleanup handler를 한번 실행한 후 제거한다.

pthread_t pthread_self(void);
 - 현재 동작중인 pthread의 식별자를 리턴한다.

+ Recent posts