7가지 동시성 모델
읽은 책에 대해 다 정리하고 있지는 못하지만...
우쨋건 지난주까지 우격다짐으로 읽어보긴 한 책이다.
출판사링크: http://www.hanbit.co.kr/store/books/look.php?p_code=B3745244799
책에 대해서는 페북의 광고와 팟캐스트 나프다에서 접했다.
오래전부터 동시성 코딩에 대해 관심이 많기는 했다.
이미 15년 전쯤? 구매해서 가장 많이 활용했던 책도 Thread 관련 책이었다.
Multithreading Applications in Win32
저 책의 저자 서문즈음에 나온다. 원문은 기억나지 않지만 대략 이런 내용이다.
"쓰레드는 반드시 네 손 위에서 쓰도록 하라"
모르거나 능력밖의 범주 혹은 통제범위 밖에서는 절대 사용하지 말라는 경고였다.
개인적인 학습의 목적이라면 저런 경고가 괜한 걱정일지 모른다.
그러나 상용 프로그래밍 개발에서는 진리와 같다.
쓸 얘기도 별로 없지만 다시 "7가지 동시성 모델"에 대해 얘기해보자.
내가 걸어온 개발자의 길에서는 상당히 생소한 책이었다.
내용은 충분히 가치가 있었다.
그러나 해당 언어들에 대해 생소하고 예제를 직접 다 실행하고 분석해보지 않은다면 와 닿지 않을 수 있다.
책에서 설명하는 언어에 대해 간단히 적어보자.
- 클로저 - Java VM 기반의 함수형 언어
- Elixir - Erlang 기반의 함수형 언어
클로저 예제를 돌려 보기 위해 이것저것 뒤져보았다.
- http://www.tryclj.com/ - Clojure REPL을 웹으로 제공해준다. 웹에서 책의 코드를 입력하고 테스트 해 볼 수 있다.
- http://clojure.or.kr/wiki/doku.php?id=lecture:clojure:why_clojure - 클로저 한글 위키
- http://www.braveclojure.com/getting-started/ - 클로저에 대한 다른 책의 내용인데 설치와 실행에 대한 부분을 참고할 수 있다.
- http://leiningen.org/ - 싸이트 설명에 나와 있는 바 대로다. "for automating Clojure projects without setting your hair on fire". 윈도우즈의 경우 인스톨 본을 통해 설치 가능하고 Clojure REPL을 설치해서 사용가능하다. 책의 예제는 여기에 손으로 타이핑하여 테스트 해 봤다
Elixir도 편하게 예제를 실행해 볼 수 없을까 찾아봤다.
- http://elixir-lang.org/install.html - Elixir 설치
- http://elixir-lang.org/getting-started/introduction.html#installation - 시작 부분으로 윈도우에서는 설치 후 iex.bat --werl 명령을 통해 얼랭의 콘솔을 이용하여 더 편리하게 작업 가능하다
'책' 카테고리의 다른 글
난 정말 JAVA를 공부한 적이 없다구요. (0) | 2016.12.07 |
---|---|
C++11 STL 프로그래밍 (0) | 2015.12.11 |
Pro Oracle Spatial for Oracle Database 11g (0) | 2011.11.15 |
오라클SQL튜닝(쿼리의 본질과 성능 튜닝에 대한 37가지 이슈) (0) | 2011.11.15 |
Head First PMP (0) | 2010.03.06 |
기존에 제작했던 pcqueue 샘플 코드
//--------------------------------------------------------------------------------------------------------------------- // pcqueue.hpp // // Asker's product consumer queue //--------------------------------------------------------------------------------------------------------------------- #pragma once #include <iostream> #include <vector> #include <pthread.h> #include "errno.h" #include "common.hpp" using namespace std; //--------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------- template <class T> class CPCQueue { private: pthread_mutex_t ProducerLock; pthread_mutex_t ConsumerLock; vector<T*> ProducerQueue; vector<T*> ConsumerQueue; private: bool LockProducer() { int _nError = pthread_mutex_lock( &ProducerLock ); if ( _nError ) { cout << "error in lock producer. return : " << _nError << ", err :" << strerror(errno) << endl; formatlog( LOG_CRIT, "error in lock producer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno)); return false; } return true; } bool UnlockProducer() { int _nError = pthread_mutex_unlock( &ProducerLock ); if ( _nError ) { cout << "error in unlock producer. return : " << _nError << ", err :" << strerror(errno) << endl; formatlog( LOG_CRIT, "error in unlock producer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno)); return false; } return true; } bool LockConsumer() { int _nError = pthread_mutex_lock( &ConsumerLock ); if ( _nError ) { cout << "error in lock consumer. return : " << _nError << ", err :" << strerror(errno) << endl; formatlog( LOG_CRIT, "error in lock consumer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno)); return false; } return true; } bool UnlockConsumer() { int _nError = pthread_mutex_unlock( &ConsumerLock ); if ( _nError ) { cout << "error in unlock consumer. return : " << _nError << ", err :" << strerror(errno) << endl; formatlog( LOG_CRIT, "error in unlock consumer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno)); return false; } return true; } public: CPCQueue(){} virtual ~CPCQueue() { for ( class vector<T*>::iterator _iter = ProducerQueue.begin(); _iter != ProducerQueue.end(); _iter++) delete *_iter; for ( class vector<T*>::iterator _iter = ConsumerQueue.begin(); _iter != ConsumerQueue.end(); _iter++) delete *_iter; ProducerQueue.clear(); ConsumerQueue.clear(); } bool Init() { int _ErrorNo = pthread_mutex_init( &ProducerLock, NULL); if (_ErrorNo) return false; _ErrorNo = pthread_mutex_init( &ConsumerLock, NULL); if (_ErrorNo) return false; return true; } // Product에 하나를 추가한다 bool AddProduct( T* _Job ) { if ( false == LockProducer()) { // lock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__); return false; } ProducerQueue.push_back(_Job); if ( false == UnlockProducer()) { // unlock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__); return false; } return true; } // Consumer 쪽에서 하나씩 빼낸다. bool PopFromConsumer(T** _Value) { *_Value = NULL; if ( false == LockConsumer()) { // lock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__); return false; } if (!ConsumerQueue.empty()) { *_Value = ConsumerQueue.front(); ConsumerQueue.erase(ConsumerQueue.begin()); } if ( false == UnlockConsumer()) { // unlock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__); return false; } return true; } // 합할 것이 하나도 없는 경우 false를 반환한다 bool MoveProducerToConsumer(int& _MoveCount) { if ( false == LockConsumer()) { // lock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__); return false; } if ( false == LockProducer()) { // lock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__); return false; } _MoveCount = ProducerQueue.size(); if (!ProducerQueue.empty()) { for ( class vector<T*>::const_iterator _iter = ProducerQueue.begin(); _iter != ProducerQueue.end(); _iter++) ConsumerQueue.push_back(*_iter); ProducerQueue.clear(); } if ( false == UnlockProducer()) { // unlock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__); return false; } if ( false == UnlockConsumer()) { // unlock 실패 formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__); return false; } return true; } int GetConsumerSize() const { return ConsumerQueue.size(); } }; //---------------------------------------------------------------------------------------------------------------------
class CDataThread: public CUbiThread { private: CPCQueue<COnePacket> PCQueue; int ThreadIndex; CURL* ctx; public: bool Terminated; public: CDataThread(); virtual ~CDataThread(); bool init(); void SetIndex(int _Index); bool AddProduct( COnePacket* _OnePacket); virtual void Run(); private: bool DoPacketProcess(COnePacket* _OnePacket); CUbiParser* MakeParser(COnePacket* _OnePacket); } //--------------------------------------------------------------------------------------------------------------------- CDataThread::CDataThread() { } //--------------------------------------------------------------------------------------------------------------------- CDataThread::~CDataThread() { } //--------------------------------------------------------------------------------------------------------------------- bool CDataThread::init() { return PCQueue.Init(); } //--------------------------------------------------------------------------------------------------------------------- void CDataThread::SetIndex(int _Index) { ThreadIndex = _Index; } //--------------------------------------------------------------------------------------------------------------------- bool CDataThread::AddProduct( COnePacket* _OnePacket ) { return PCQueue.AddProduct(_OnePacket); } //--------------------------------------------------------------------------------------------------------------------- void CDataThread::Run() { int _Count = 0; int _JobCount = 0; int _SleepMicro; COnePacket* _OnePacket = NULL; Terminated = false; while(!Terminated) { try { // Customer Queue에 Producer Queue의 내용을 가져오게 한다 if (!PCQueue.MoveProducerToConsumer(_Count)) formatlog( LOG_FATAL, "CDataThread::%s(%d) %10d Thread Error in Move PCQueue data", __func__, __LINE__, ThreadIndex); //, typeid(T).name()); else formatlog( LOG_INFO, "CDataThread::%s(%d) %10d Thread %2d %3d remain.", __func__, __LINE__, ThreadIndex, _Count, PCQueue.GetConsumerSize()); } catch(exception &_innerException) { formatlog( LOG_CRIT, "Exception on CDataThread::%s(%d). %2d Thread what(%s)", __func__, __LINE__, ThreadIndex, _innerException.what()); } // Customer Queue를 비울때 까지 작업을 처리 한다 _JobCount = 0; while(true) { _OnePacket = NULL; // 처리 간격에 일정 시간을 둔다 if (!PCQueue.PopFromConsumer(&_OnePacket)) { formatlog( LOG_FATAL, "CDataThread::%s(%d). %2d Thread Error in Get Data From PCQueue", __func__, __LINE__, ThreadIndex); break; } if ( NULL == _OnePacket) break; //1 실질적인 패킷 처리!! DoPacketProcess(_OnePacket); _JobCount++; delete _OnePacket; } usleep(500000 ); } }
'프로그래밍??? > C/C++' 카테고리의 다른 글
Dual ABI 이슈 - GCC5 (0) | 2016.08.05 |
---|---|
POCO Library link 이슈 - GCC 5.4.0 업버전 후 생긴 문제 (0) | 2016.08.04 |
Bad cast exception on poco-library when I tried to cast Int64 (0) | 2015.07.30 |
Boost를 활용해 공유 메모리에 STL Container 사용하는 방법 (0) | 2015.07.10 |
Creating STL Containers in Shared Memory (0) | 2015.07.10 |
multi thread에서 Mutex 사용시 에러...
Producer Consumer 큐를 만들었다.
이와 함께 Job을 가상화 하여 일정한 규칙(동일 DB Insert)을 가지는 다양한 Job을 처리하게 하였다.
그리고 그것도 템플릿으로 또 다시 묶었다. (너무 과잉인가.. ㅠ.ㅠ)
문제는 테스트 코드에서는 문제가 없는데...
상용 코드에서는 자꾸 에러가 나는 것이다.
pthread_mutex_lock()을 콜하면 "130"을 반환하고 errno로 "0"을 줬다.
프로그램은 lock이 걸려서 더 진행되지 않았다.
스택오버플로우도 뒤져보고 해서...
뭐 테스트 쓰레드도 만들고 다 해봤는데 또 잘 된다.
결론은!!!!
상용코드에서 init()을 콜하지 않았다. ㅠ.ㅠ
해당 init 코드에서 pthread_mutex_init()을 통해 mutex를 초기화 해야 하는데
초기화 하지 않은 mutex를 사용하려니 문제를 발생 시킨 것이다.
그런데 그게 다른 에러가 아니라... 130번이 반환되서 고생한 것이었다. ㅠ.ㅠ
"EOWNERDEAD"
'프로그래밍??? > C/C++' 카테고리의 다른 글
Creating STL Containers in Shared Memory (0) | 2015.07.10 |
---|---|
공유 메모리에 STL Container를 올리려고 하게 된 연유 (0) | 2015.07.10 |
AES의 block mode 관련 사항 (0) | 2015.05.08 |
AES 암호/복호 C/C++ 기능 구현 (1) | 2015.05.06 |
Producer/Consumer 큐 구현하기(std::vector와 Template를 함께 사용하기) (1) | 2015.02.24 |