상세 컨텐츠

본문 제목

[boost] asio#2

똑똑한 개발/C++

by 성댕쓰 2022. 5. 2. 22:43

본문

strand를 이용, completion handler를 multithreaded program에서 동기화하는 방법을 알아보자.

completion handler는 io_context::run()을 실행한 스레드에서 실행된다. 하나의 스레드에서만 run()을 호출 했다면, 핸들러는 절대 동시에 실행되지 않는다.

 

싱글 스레드에서만 asio를 사용하여 앱을 만드는 것은 좋지만 다음과 같은 제약이 있다.

  - 핸들러 작업이 오래걸리면, 반응성이 느려짐.

  - 멀티 프로세스 활용 못함.

 

대안으로 io_context::run()을 콜하는 스레드 풀을 만드는 방법이 있다.

그러나, 핸들러에서 공유 자원에 접근하는 등 스레드 safe하지 않기 때문에 동기화 작업이 필요하다.

 

io_context를 받아 멤버변수 strand를 초기화 하는 클래스를 만든다.

class Printer
{
public:
	Printer(boost::asio::io_context& io)
		: strand_(boost::asio::make_strand(io)),
		  timer1_(io, boost::asio::chrono::seconds(1)),
		  timer2_(io, boost::asio::chrono::seconds(1)),
		  count_(0)
	{
		timer1_.async_wait(boost::asio::bind_executor(strand_,
			boost::bind(&Printer::print1, this)));

		timer2_.async_wait(boost::asio::bind_executor(strand_,
			boost::bind(&Printer::print2, this)));
	}
    ...
}

strand class는 executor adaptor이다.

이를 이용 handler를 dispatch하면 handler 실행은 다음 핸들러가 실행 되기전에 완료된다.

다수의 스레드가 io_context::run()을 호출하여도 핸들러의 동기화를 보장한다.

물론, strand로 dispatch 하지 않은 핸들러나 다른 strand object로 dispatch하면 다른 스레드에서 동시에 실행된다.

 

	...
    ~Printer()
	{
		std::cout << "Final count is " << count_ << std::endl;
	}
	void print1()
	{
		if (count_ < 100)
		{
			std::cout << "Timer 1: " << count_ << std::endl;
			++count_;

			timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1));

			timer1_.async_wait(boost::asio::bind_executor(strand_,
				boost::bind(&Printer::print1, this)));
			/*timer1_.async_wait(
				boost::bind(&Printer::print1, this));*/
		}
	}

	void print2()
	{
		if (count_ < 100)
		{
			std::cout << "Timer 2: " << count_ << std::endl;
			++count_;

			timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1));

			timer2_.async_wait(boost::asio::bind_executor(strand_,
				boost::bind(&Printer::print2, this)));
			/*timer2_.async_wait(
				boost::bind(&Printer::print2, this));*/
		}
	}

private:
	boost::asio::strand<boost::asio::io_context::executor_type> strand_;
	boost::asio::steady_timer timer1_;
	boost::asio::steady_timer timer2_;
	int count_;
};

 

int main()
{
	boost::asio::io_context io;
	Printer p(io);
	boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
	io.run();
	t.join();

	return 0;
}

싱글 스레드에서와 마찬가지로, io_context::run() 실행하는 스레드 모두, 실행할 work가 없으면 종료한다.

 

 

참조 : Timer.5 - Synchronising completion handlers in multithreaded programs - 1.79.0 (boost.org)

'똑똑한 개발 > C++' 카테고리의 다른 글

DllMain 정리  (0) 2022.08.24
Strand 정리  (0) 2022.08.23
[boost] asio #1  (0) 2022.05.01
[Visual Studio] Character Set Unicode vs MBCS  (0) 2022.04.26
enable_shared_from_this  (0) 2022.04.22

관련글 더보기

댓글 영역