기본 콘텐츠로 건너뛰기

TLS - Thread Local Storage

쓰레드를 돌리다보면 해당 쓰레드에서만 유효한 변수를 만들고 싶을 때가 있다. 그럴 때 어떻게 해야할까? 쓰레드 개수만큼 벡터를 잡고, 해당 쓰레드마다 벡터 인덱스를 넘겨주는 것도 나름 방법이지만, 이러한 경우를 위해 TLS라는 것이 존재한다. 축약처럼 쓰레드 안에서만 따로 스토리지를 할당한 것인데, 일반 프로세스의 스텍과 비슷하나 그 크기는 프로세스 것보다 당연히 작고 제약이 심한 편이다. 뭐 암튼 그런게 있다고 치자.

* GCC 메뉴얼 보면 TLS가 아니라 TSD(Thread Storage Duration)이라고 표현하였는데 대충 같은 말이다.

이것을 위해 POSIX는 pthread_key_create, pthread_get/setspecific 등 함수를 마련해놨지만 눈만 팽글팽글 돌고, 소스만 지저분해져 보일 것 같다. 귀찮으면 C99, C++98 표준 __thread 키워드를 사용하자. MSDN에 보니 __thread가 없어 보인다. 이가 없으면 잇몸으로라도...

#define __thread __declspec( thread )

대충 이 정도면 될 듯하다. 자자 각설하고, __thread란 키워드는 TLS에 변수를 선언해주는 일을 한다. 위에 나열한 pthread_* 함수를 호출하는 것으로 컴파일러가 대신 해석해주는 것일 수도 있으나 이건 가정이므로 넘어가고. 사용에 주의해야할 점은 아래와 같다. (대충대충 표준하고 MSDN 보면서 정리한 것임)
  1. 글로벌 변수나 static 변수에만 사용 가능하다.
    - function 안에 local 변수가 static이 아니라면 뭐더게 TLS에 저장할까? 당연히 그럴 필요가 없기 때문에 저런 제약이 있는 것이다. 이는 class에서 멤버 변수에도 적용한 법칙이다. static으로 선언한 멤버 변수에만 저 키워드를 사용할 수 있다.
  2. SO(DLL)과 로컬 영역을 넘나들면 뻑난다.
    - ... 그러니까 쓰지마라. 왜?
  3. 타입에 대한 한정자로 사용이 불가능하다.
    - const처럼 쓰지 말라는 것이고, 무조건 타입 앞에 붙이란 말이다. (VC에만 해당)
  4. 객체 이름과 이름 없는 객체에만 쓸 수 있다.
  5. 앞에 붙을 수 있는 건 static과 extern 뿐이다.
  6. 클래스 자체를 TLS에 넣을 때는 생성자와 소멸자가 없어야 한다.
역시나 예가 없으면 아무 것도 할 수 없는 나를 위하여~*
#include <iostream>
using namespace std;

__thread int gTest1;      // 성공

__thread void func(void); // 실패

void
func(__thread int lTest2) // 실패
{
}

class A
{
    static __thread int mTest3;   // 성공
    __thread int mTest4;          // 실패
};

int
func(void)
{
    static __thread int lTest5; // 성공
    __thread int lTest6;   // 실패
}
__thread union
{
    int i;
    char p[sizeof(int)];
} gTest7;   // 성공
__thread struct
{
    int i;
    char p[sizeof(int)];
} gTest8;   // 성공

class MyClass
{
public:
    int i;
    MyClass() i(0) {}
};

__thread MyClass cTest9; // 실패

int __thread * gTest9; // gcc 성공

int
main(int,char**)
{
    return 0;
}

나머진 시간 나는대로 정리하도록 해야겠다.

댓글

이 블로그의 인기 게시물

버즈 라이브 배터리 교체

나는 버즈 라이브(SM-R180)가 좋은데, 평가가 별루였는지, 해당 스타일로 버즈를 더 이상 만들지 않고 있다. 아무튼, 오래 쓴 버즈 라이브 배터리가 슬슬 맛이 가기 시작해서, 블로그 를 참조하면서 분해 및 교체를 하였다. (진짜 쉬움) 요로코롬 위아래를 살짝 눌러주면 뚜껑이 벌어진다. 안쪽 플라스틱은 오른쪽은 분홍색, 왼쪽은 회색이다. 리본 케이블 살짝 들어내고, 기판을 떼어내면, 작은 나사가 있다. 나사를 풀고, 플라스틱을 걷어내면, 검은 양면 테이프로 고정된 CR1254 배터리가 보인다. 잘 쑤셔서(?) 꺼낸다. 새로운 CR1254 배터리를 넣는다. 음극이 아래로 가도록 하고, 분해의 역순으로 조립하면 된다. 조립할 때, 아까 풀었던 나사는 잊지 말고 꼭 조여준다. (까먹고 조립해서 다시 뜯고 조립함) 충전도 잘 되고, 소리도 잘 나는거 보면, 조립도 잘 된 것 같다. 이렇게 버즈 라이브의 수명을 강제로 늘렸다. 나중에 본체 배터리도 갈아야겠다.

ESP8266 + TM1637 WIFI 인터넷 시계 만들기

ESP8266 과 TM1637 으로 인터넷 시계 만들기 원래는 다이소에서 판매하는 5000원짜리 시계 뜯어서 만드려고 했는데, 시계를 뜯어보니, 자체 MCU를 통해 제어를 하고 있어 TM1637 로 구현하고 나중에 케이스를 만드는게 나을 것 같아서 아래와 같이 구현(chatGPT)했다. 30분마다 NTP 가져오기 WIFI가 끊기더라도 RTC 유지 WIFI가 끊기면 1분에 한 번씩 재접속 시도 버튼을 3초 누르면, AP 설정 모드 (88:88 표시) #include <ESP8266WiFi.h> #include <WiFiUdp.h> #include <TM1637Display.h> #include <WiFiManager.h> // https://github.com/tzapu/WiFiManager #define CLK D5 // TM1637 CLK #define DIO D6 // TM1637 DIO #define CONFIG_BUTTON_PIN D7 // 설정 진입 버튼 TM1637Display display(CLK, DIO); // 시간 관련 변수 time_t currentEpochTime = 0; unsigned long lastNtpMillis = 0; unsigned long checkTermMillis = 1800000UL; // 30분마다 NTP 다시 받아옴 // Wi-Fi 재접속 관련 unsigned long lastWiFiReconnectAttempt = 0; const unsigned long wifiReconnectInterval = 60000; // 1분 // 버튼 관련 unsigned long buttonPressedTime = 0; bool buttonWasHeld = false; const unsigned long CONFIG_HOLD_TIME = 3000; // 3초 bool isInConfigMode = false; const char* ntpServer = ...

Windows 에서 절전을 깨우는 장치 찾기

참조:  https://www.reddit.com/r/computer/comments/wquswv/windows_11_pc_wakes_up_every_time_i_move_usb/ powercfg /devicequery wake_armed powercfg /deviceenablewake "[DEVICE]" # $PROFILE function Get-WakeArmedDevices { $devices = powercfg -devicequery wake_armed if ($devices) { $devices | ForEach-Object { $_.Trim() } } else { Write-Host "No devices are currently armed for wake events." } } function Set-EnableWakeOnDevice { param( [string]$deviceName ) sudo powercfg -deviceenablewake $deviceName } function Set-DisableWakeOnDevice { param( [string]$deviceName ) sudo powercfg -devicedisablewake $deviceName }