'Operations Dev/Producer'에 해당되는 글 2건

  1. 2010.12.11 종료
  2. 2010.12.10 Producer / Customer
posted by Kyleslab 2010. 12. 11. 01:39


이서비스는 쓰레드로 만들었습니다.
그리고 윈도우는 메인함수에서 메인쓰레드라는게 존재한다.
그리고 각각 동작한다.

그리고 아까이야기 했듯이 클래스단위로 쓰레드화 하는게 아니고
하나의 컨숨?한 쓰레드가 됩니다.
이 프로듀서를 어디서 생성을 할까요->메인에서 생성할겁니다.
끄려면 강제종료를 시켜야하는데 그러면 메인이 종료됩니다.
그러면서 자식쓰레드들도 종료가됩니다.

그럼 소멸자가 어디서 불릴까? ->메인스레드
서비스함수들은 어디서 돌아가고 있을까? -> 각각 자기스레드

그럼 소멸자가 호출되는 순간 그 스레드들은 돌고있을까 안돌고있을까?
-> 돌고있고 소멸자가 호출되도 돌리려고 시도 할 수 있다. -> 그러면 터진다

해결책
플래그변수를 불러요 -> Customer에 boolean변수 isEnd를 만든다
volatile -> 최적화 안하겠다 -> 캐싱하지 않겠다 ->멀티코어에서 발생할수있는 버그를 없애겠다
-> 메모리에 있는 변수 캐쉬에 올리지마. 느려도 걍 쓸테니
->컴파일러의 코드순서바꾸기도 방지

캐시 아이오 속도차이 를 극복하기위해 각각 자신이 사용하는 변수를 캐싱한다. 메모리의 복사본을
캐시메모리에 올린다. 하나의 메모리를 두개의 시피유에서 사용한다.

그리고 isEnd를 false로 세팅하고 각각의 소멸자에 true로 바꿔준다.
서비스는 isEnd가 Ture일때까지 돌도록 바꾼다

isend가 한번더 돌다가 종료할때

->>>>>>>>>> 하지만 그래도 터진다.

서로 스레드가 다르므로 생산자로 넘어갔을때 한번 실행하고 돌아간다.
이 미묘한 타이밍때문에 똑같이 터진다.

>>>>>>>>>>>>>>>>>> while루프를 빠져나와서 온순간까지 락걸어준다.

소멸자에서 wait코드가 있는거고 while루프 바깥에서 풀어주고
생성자는 리소스에 대해서 락걸고 while루프 바깥에서 풀어버리면 세이프한 코드가 된다.

. 저번주에 한 생산자/소비자 실습에서 프로그램 종료시에 에러가 날 수 있는 조건과 해결책에 대한 필기 문제
메인함수에서 프로듀서와 커스토머 쓰레드를 만들고
메인쓰레드가 종료될때 아직 이 두개의 쓰레드는 각자 자기 쓰레드에서 돌고 있는데, 메인쓰레드를 종료되면 터진다.
왜냐하면 메인쓰레드가 종료될때 소멸자가 호출되는데 이 호출되는 순간에도 각자 자신의 쓰레드가 돌고 있기 때문이다. 그러므로 메인쓰레드가 종료될때 소멸자가 호출되는것을 이용하여 소멸자가 호출되었을대
모든 자식 스레드를 종료해주기 위해 자신의 스레드가 종료되도록하게 한다. 그리고 그다음 메인스레드가 종료되게하면 문제가 해결된다.
::CloseHandle( ( HANDLE )( _threadHandle ) );

메인스레드가 종료될때 소멸자가 호출되는데
Thread::~Thread()
{
 ::WaitForSingleObject( ( HANDLE )( _threadHandle ), INFINITE); //반드시해주어야함 모든 자식 스레드를 종료해주기 위해 메인스레드가 종료하면 소멸자를 호출하게 되고 자신의 핸들스레드가 종료될때까지 기다리게 한다.!!!
 ::CloseHandle( ( HANDLE )( _threadHandle ) ); // 그다음에 메인 스레드를 종료한다.
}
내가 만든 스레드가 돌고 있는 상황에서 메인스레드가
종료 되버리면 에러가 난다.

메인스레드가 종료될때 클래스의 소멸자를 호출하게되면
소멸자에 지금 돌고있는 스레드가 모두 종료될때 까지 기다리게 한다.

스핀락을 사용하는 이유는 리소스를 사용? 대기? 하는 것이 컨텍스트스위칭이 일어나는것보다 빠르기 때문

'Operations Dev > Producer' 카테고리의 다른 글

Producer / Customer  (0) 2010.12.10
posted by Kyleslab 2010. 12. 10. 23:45


Producer.h

#pragma once

#include <queue>
#include <string>

#include "Concurrency\Thread.h"
#include "Customer.h"
#include "Concurrency\Timer.h"
#include "Concurrency\SpinLock.h"

class Producer
{
public:
 Producer(float frequency);
 ~Producer();


public:
 void
  beginService();
 bool
  tryPop( std::string * product);
 
private:
 void service();

private:
 Thread * _thread;
 std::queue< std::string > _products;//경쟁관계에 있는 녀석
 Timer _timer;
 float _frequency;
 unsigned int _count;
 volatile bool _isEnd;
 SpinLock mylock;
};


Produce.cpp

#include "StdAfx.h"
#include "Producer.h"

#include <Windows.h>


Producer::Producer(float frequency)//인자를 갖지 않는 생성자를 호출한다 기입하지 않으면, 파라미터를 받는 생성자를 만들었다면 기본생성자는 호출되지않는다.
 : _count( 0 ),
 _isEnd(false),
 _frequency(frequency)
{
 
}

void Producer::beginService()
{
 _thread = new Thread( std::bind(&Producer::service, this));
}

Producer::~Producer(void)
{
 _isEnd = true;
 ::WaitForSingleObject( ( HANDLE )( _thread ), INFINITE); //반드시해주어야함 모든 자식 스레드를 종료해주기 위해 메인스레드가 종료하면 소멸자를
 delete _thread;

 // 메인스레드가 종료될때 소멸자 호출,. 그러나 지금 스레드가 작동중이라면???
 

}

bool Producer::tryPop( std::string * product)
{
 mylock.lock();
 bool result = false;

 if( !_products.empty())
 {
  *product = _products.front();
  _products.pop();
  result = true;
 }
 mylock.unlock();
 return result;

}

 

void Producer::service()

 _timer.start();
 
 while( !_isEnd )
 {
   
  if(_timer.elapsedSeconds() >= _frequency)
  {
   
   char number[100];
   itoa( _count, number, 10 );
   
   std::string product = "product";
   product += number;
   ++_count;
   _timer.stop();
   _timer.start();
   mylock.lock();
   _products.push( product );
   mylock.unlock();
  }
  
  ::Sleep(0); //원래는 블록큐로 가나, 0이니까 준비큐로가고 1초가 아닐때 다른작업할거있음 하라고 양보하는 것이다.
 }
 
}

Customer.h
#pragma once

#include "Concurrency\Thread.h"
#include "Concurrency\Timer.h"
#include "Concurrency\MutexLock.h"

class Producer;

class Customer
{
public:
 Customer( Producer * producer, // 포인터 타입으로 갖고 있게 한다. 인스턴스를 선언하지 않고, 클래스실체를 선언하지 않고 사용하는법
        float frequency);
 ~Customer();

public:
 void
  beginService();

private:
 void service();//백그라운드에서
 
 private:
 Thread * _thread;
 Producer * _producer;
 Timer _timer;
 float _frequency;
 volatile bool _isEnd;


};


Customer.cpp
#include "StdAfx.h"
#include <concrt.h>
#include <Windows.h>


#include "Customer.h"

 

#include "Producer.h"//헤더에서 그냥 선언만해놓고 여기서 추가해주면 사용할수 있다.


Customer::Customer( Producer * producer,
 float frequency)
 : _producer( producer ),
 _frequency( frequency ),
 _isEnd(false)
{

}


Customer::~Customer(void)
{
 _isEnd = true;
 ::WaitForSingleObject( ( HANDLE )( _thread ), INFINITE);
 delete _thread;
}


void Customer::beginService()
{
 _thread = new Thread( std::bind(&Customer::service, this));
}


void
 Customer::service()
{
 _timer.start();

 while( !_isEnd)
 {
  if(_timer.elapsedSeconds() >= _frequency )
  {
   std::string product;

   if( _producer->tryPop(&product)) // 커스토머 쓰레드 호출되는 곳의 쓰레드이다.
   {
 
    printf( "%s\n", product.c_str() );

    _timer.stop();
    _timer.start();
   }

  }
  ::Sleep( _frequency * 250 );
 }
}


ServiceThread.cpp
// ServiceThread.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"

#include<Windows.h>

#include "Customer.h"
#include "Producer.h"
int _tmain(int argc, _TCHAR* argv[])
{
 Producer p(0.01f);
 Customer c1( &p, 0.008);
 Customer c2( &p, 0.005 );

 p.beginService();
 ::Sleep( 2000 );
 c1.beginService();
 c2.beginService();

 Timer timer;
 timer.start();
 while( timer.elapsedSeconds() < 20);

 return 0;
}

 

'Operations Dev > Producer' 카테고리의 다른 글

종료  (0) 2010.12.11