C++, Linux, Multithreading 환경에서 std::sleep_for(std::chrono::hours::max())가 즉시 반환되는 이유

2024-07-27

std::sleep_for 함수의 작동 방식

std::sleep_for 함수는 지정된 시간 동안 스레드를 일시 중단시킵니다. std::chrono::hours::max() 값은 24855 years, 23 hours, 59 minutes, 59 seconds, 999 milliseconds를 나타내는 std::chrono::duration 객체입니다. 이 값을 std::sleep_for 함수에 전달하면 스레드가 약 24855년 동안 일시 중단될 것으로 기대됩니다.

Linux 시스템의 nanosleep 시스템 콜

std::sleep_for 함수는 Linux 시스템에서 nanosleep() 시스템 콜을 사용하여 스레드를 일시 중단시킵니다. nanosleep() 시스템 콜은 두 개의 인수를 받습니다:

  • requested_time: 스레드가 일시 중단될 시간을 나타내는 구조체
  • remaining_time:

requested_time 값이 timespec 구조체에 저장됩니다. timespec 구조체는 다음 두 가지 필드로 구성됩니다:

  • tv_sec: 초 단위의 시간

std::chrono::hours::max() 값과 nanosleep 시스템 콜

std::chrono::hours::max() 값은 timespec 구조체에 저장할 수 있는 값보다 훨씬 큽니다. timespec 구조체에서 tv_sec 필드는 32비트 정수로 표현되므로 최대 2147483647초 (약 68년)까지만 저장할 수 있습니다. std::chrono::hours::max() 값은 이 값을 훨씬 초과합니다.

따라서 std::sleep_for(std::chrono::hours::max()) 함수를 호출하면 nanosleep() 시스템 콜은 requested_time 값을 timespec 구조체에 저장할 수 없고 즉시 반환됩니다.

문제 해결 방법

std::sleep_for(std::chrono::hours::max()) 함수가 즉시 반환되는 문제를 해결하려면 다음과 같은 방법을 사용할 수 있습니다:

  • std::this_thread::sleep_for 함수 사용:

std::this_thread::sleep_for 함수는 현재 스레드를 지정된 시간 동안 일시 중단시킵니다. 이 함수는 std::chrono::hours::max() 값을 인수로 받을 수 있습니다.

std::this_thread::sleep_for(std::chrono::hours::max());
  • while 루프 사용:

while 루프를 사용하여 지정된 시간 동안 반복적으로 잠을 자도록 스레드를 구현할 수 있습니다.

while (true) {
  std::this_thread::sleep_for(std::chrono::seconds(1));
  // ...
}
  • 조건 변수 사용:

조건 변수를 사용하여 스레드가 특정 조건이 충족될 때까지 잠을 자도록 구현할 수 있습니다.

std::mutex mtx;
std::condition_variable cv;

void thread_func() {
  std::unique_lock<std::mutex> lock(mtx);
  cv.wait(lock, [] { return condition_is_met(); });
  // ...
}



예제 코드

#include <iostream>
#include <chrono>

int main() {
  std::cout << "Starting..." << std::endl;

  std::this_thread::sleep_for(std::chrono::seconds(10));

  std::cout << "Finished!" << std::endl;

  return 0;
}

이 코드를 실행하면 다음과 같은 출력이 생성됩니다.

Starting...
Finished!

다음은 while 루프를 사용하여 10초 동안 반복적으로 잠을 자도록 스레드를 구현하는 C++ 코드 예시입니다.

#include <iostream>
#include <chrono>

int main() {
  std::cout << "Starting..." << std::endl;

  int i = 0;
  while (i < 10) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    i++;
  }

  std::cout << "Finished!" << std::endl;

  return 0;
}
Starting...
Finished!
#include <iostream>
#include <chrono>
#include <mutex>
#include <condition_variable>

bool condition_is_met = false;

std::mutex mtx;
std::condition_variable cv;

void thread_func() {
  std::unique_lock<std::mutex> lock(mtx);
  cv.wait(lock, [] { return condition_is_met; });

  // ...
}

int main() {
  std::cout << "Starting..." << std::endl;

  std::thread t(thread_func);

  // ...

  {
    std::unique_lock<std::mutex> lock(mtx);
    condition_is_met = true;
  }
  cv.notify_one();

  t.join();

  std::cout << "Finished!" << std::endl;

  return 0;
}
Starting...
Finished!

주의 사항




C++에서 스레드를 일시 중단시키는 대체 방법

std::this_thread::sleep_for 함수

std::this_thread::sleep_for 함수는 현재 스레드를 지정된 시간 동안 일시 중단시킵니다. 이 함수는 가장 간단하고 직관적인 방법입니다.

std::this_thread::sleep_for(std::chrono::seconds(10));

while 루프

int i = 0;
while (i < 10) {
  std::this_thread::sleep_for(std::chrono::seconds(1));
  i++;
}

조건 변수

std::mutex mtx;
std::condition_variable cv;

bool condition_is_met = false;

void thread_func() {
  std::unique_lock<std::mutex> lock(mtx);
  cv.wait(lock, [] { return condition_is_met; });

  // ...
}

int main() {
  std::thread t(thread_func);

  // ...

  {
    std::unique_lock<std::mutex> lock(mtx);
    condition_is_met = true;
  }
  cv.notify_one();

  t.join();

  return 0;
}

boost::thread::sleep 함수

Boost C++ 라이브러리를 사용하면 boost::thread::sleep 함수를 사용하여 스레드를 일시 중단시킬 수 있습니다.

#include <boost/thread.hpp>

boost::thread::sleep(boost::posix_time::seconds(10));

std::atomic 클래스

std::atomic 클래스를 사용하여 원자성을 유지하면서 스레드를 일시 중단시킬 수 있습니다.

std::atomic<bool> flag = false;

void thread_func() {
  while (!flag) {
    // ...
  }

  // ...
}

int main() {
  std::thread t(thread_func);

  // ...

  flag = true;

  t.join();

  return 0;
}

선택 가이드

사용할 방법은 상황에 따라 다릅니다.

  • 간단한 경우 std::this_thread::sleep_for 함수를 사용하는 것이 가장 좋습니다.
  • 더 정교한 제어가 필요한 경우 while 루프나 조건 변수를 사용하는 것이 좋습니다.
  • 원자성을 유지해야 하는 경우 std::atomic 클래스를 사용해야 합니다.

c++ linux multithreading



C++에서 switch 문에서 변수를 선언할 수 없는 이유

이것에는 몇 가지 중요한 이유가 있습니다.1. 스택 프레임 관리:C++에서 함수나 블록을 호출할 때마다 메모리 스택에 프레임이 생성됩니다. 이 프레임에는 해당 함수 또는 블록에서 사용되는 변수와 임시 데이터가 저장됩니다...


C++에서의 "Strict Aliasing Rule" 란 무엇일까요?

이 규칙은 다음과 같은 상황에 적용됩니다.서로 다른 기본 유형을 가진 포인터: int* 포인터와 char* 포인터는 서로 다른 유형으로 간주되므로 별칭이 허용되지 않습니다.const 또는 volatile 키워드가 달라지는 포인터: const int* 포인터와 int* 포인터는 서로 다른 유형으로 간주되므로 별칭이 허용되지 않습니다...


C++에서 스마트 포인터란 무엇이며 언제 사용해야 할까요?

1. 자동 메모리 해제:스마트 포인터는 소멸자를 통해 자동으로 메모리를 해제하기 때문에 메모리 누수를 방지하는 데 도움이 됩니다. 일반 포인터를 사용하는 경우 프로그래머가 직접 메모리를 해제해야 하기 때문에 누수가 발생하기 쉽습니다...


C++ 및 C 언어에서 구조체 크기 계산: sizeof 연산자의 비밀

1. 메모리 정렬:컴파일러는 메모리 접근 속도를 최적화하기 위해 데이터를 특정 방식으로 정렬합니다. 이는 구조체 멤버의 배치에도 영향을 미칩니다.예를 들어, 다음 구조체를 살펴보겠습니다.int는 일반적으로 4바이트...


C++ 상속에서 생성자 호출 규칙

1. 기본 클래스 생성자 우선 호출:파생 클래스 객체를 생성하면 먼저 기본 클래스 생성자가 호출됩니다. 즉, 파생 클래스의 생성자 코드가 실행되기 전에 기본 클래스의 생성자가 실행되어 기본 클래스 멤버 변수를 초기화합니다...



c++ linux multithreading

C/C++ 프로그래밍에서 #include <filename>과 #include "filename"의 차이점

1. #include <filename>각 컴파일러마다 정의된 표준 헤더 파일을 포함하는 데 사용됩니다.<filename> 안에 작성된 파일 이름은 컴파일러가 미리 정의된 경로 목록에서 검색됩니다. 이 목록은 일반적으로 운영 체제 및 컴파일러에 따라 다릅니다


C++에서의 일반 캐스트, 정적 캐스트, 동적 캐스트 비교: 포인터 캐스팅 심층 분석

일반 캐스트는 C++에서 가장 강력한 캐스팅 유형으로, 다양한 형식 변환을 수행할 수 있습니다. 하지만 다른 캐스팅 유형에 비해 안전성이 낮고 오류 가능성이 높다는 단점이 있습니다. 일반 캐스트는 다음과 같은 용도로 사용됩니다


C++/C에서 비트 조작: 특정 비트 설정, 해제, 토글하기

C++와 C 프로그래밍에서 비트 조작은 저수준 시스템 프로그래밍이나 효율적인 알고리즘 구현에 필수적인 기술입니다. 특히, 특정 비트를 설정, 해제, 또는 토글하는 작업은 하드웨어 제어, 데이터 압축, 암호화 등 다양한 분야에서 활용됩니다


C++에서 클래스와 구조체 사용 시점

1. 기본 접근 지정자:구조체: 기본적으로 모든 멤버가 public으로 접근 가능합니다. 즉, 외부 코드에서 쉽게 변경될 수 있습니다.클래스: 기본적으로 모든 멤버가 private으로 접근 제한됩니다. 외부 코드에서 직접 액세스를 제한하고 데이터 은닉을 통해 코드 보안을 강화합니다


C++에서 포인터 변수와 참조 변수의 차이점

1. 선언:포인터 변수: 변수 이름 뒤에 * (별표)를 사용하여 선언합니다.참조 변수: 변수 이름 뒤에 & (앰퍼샌드)를 사용하여 선언합니다.2. 초기화:포인터 변수: 선언 시 nullptr로 초기화하거나 다른 메모리 위치의 주소로 초기화해야 합니다