๐Ÿงต ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์ €์žฅ์†Œ(TLS) ๊ตฌํ˜„ํ•˜๊ธฐ: C++ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๋น„๋ฐ€ ๋ฌด๊ธฐ ๐Ÿ› ๏ธ

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿงต ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์ €์žฅ์†Œ(TLS) ๊ตฌํ˜„ํ•˜๊ธฐ: C++ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๋น„๋ฐ€ ๋ฌด๊ธฐ ๐Ÿ› ๏ธ

 

 

2025๋…„ 3์›” ๊ธฐ์ค€ ์ตœ์‹  C++ ํ‘œ์ค€์„ ๋ฐ˜์˜ํ•œ TLS ๊ตฌํ˜„ ๊ฐ€์ด๋“œ

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ง„์งœ ๊ฟ€์žผ ์ฃผ์ œ๋กœ ์ฐพ์•„์™”์–ด์š”~ ๋ฐ”๋กœ ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์ €์žฅ์†Œ(Thread Local Storage, TLS)์— ๋Œ€ํ•ด ํ•จ๊ป˜ ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”! ๐Ÿ˜Ž

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•  ๋•Œ ๊ฐ€๋” "์•„ ์ด๊ฑฐ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜์ง€?" ํ•˜๋Š” ์ˆœ๊ฐ„ ์žˆ์ž–์•„์š”? ๊ทธ๋Ÿด ๋•Œ TLS๊ฐ€ ์ง„์งœ ๊ตฌ์„ธ์ฃผ์ฒ˜๋Ÿผ ๋“ฑ์žฅํ•œ๋‹ค๋‹ˆ๊นŒ์š”! ใ…‹ใ…‹ใ…‹ ํŠนํžˆ C++ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ์ด ๊ฐœ๋… ํ™•์‹คํžˆ ์•Œ์•„๋‘๋ฉด ๋‚˜์ค‘์— ์ง„์งœ ๋งŽ์€ ๋„์›€์ด ๋œ๋‹ต๋‹ˆ๋‹ค!

์ด ๊ธ€์—์„œ๋Š” TLS์˜ ๊ฐœ๋…๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ C++์—์„œ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€, ์‹ค์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€๊นŒ์ง€ ์‰ฝ๊ณ  ์žฌ๋ฐŒ๊ฒŒ ์•Œ์•„๋ณผ๊ฒŒ์š”. ์ฝ”๋“œ ์˜ˆ์ œ๋„ ๋งŽ์ด ์ค€๋น„ํ–ˆ์œผ๋‹ˆ ๋”ฐ๋ผ์˜ค์‹œ๋ฉด์„œ ์‹ค์Šตํ•ด๋ณด์„ธ์š”! ๐Ÿ’ช

๐Ÿ“š ๋ชฉ์ฐจ

  1. ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์ €์žฅ์†Œ(TLS)๋ž€?
  2. ์™œ TLS๊ฐ€ ํ•„์š”ํ•œ๊ฐ€์š”?
  3. C++์—์„œ TLS ๊ตฌํ˜„ ๋ฐฉ๋ฒ•
  4. TLS ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ
  5. ์‹ค์ „ ์˜ˆ์ œ: TLS๋ฅผ ํ™œ์šฉํ•œ ๋กœ๊น… ์‹œ์Šคํ…œ
  6. ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ
  7. C++20/23์—์„œ์˜ TLS ๊ด€๋ จ ๋ณ€ํ™”
  8. ๋งˆ๋ฌด๋ฆฌ ๋ฐ ์ถ”๊ฐ€ ์ž๋ฃŒ

1. ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์ €์žฅ์†Œ(TLS)๋ž€? ๐Ÿค”

์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์ €์žฅ์†Œ(TLS)๋Š” ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ๋งŒ์˜ ๋…๋ฆฝ์ ์ธ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด์—์š”. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๋”๋ผ๋„ ๊ฐ์ž ์ž๊ธฐ๋งŒ์˜ '๋น„๋ฐ€ ๊ธˆ๊ณ '๋ฅผ ๊ฐ–๋Š” ๊ฑฐ์ฃ ! ใ…‹ใ…‹

์ผ๋ฐ˜ ์ „์—ญ ๋ณ€์ˆ˜๋Š” ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ ํ•˜์ž–์•„์š”? ๊ทธ๋ž˜์„œ ๋™์‹œ์— ์ ‘๊ทผํ•˜๋ฉด ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค(data race)๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ทผ๋ฐ TLS๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์ด๋ฆ„์˜ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด๋„ ์‹ค์ œ๋กœ๋Š” ๊ฐ์ž ๋‹ค๋ฅธ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ต๋‹ˆ๋‹ค! ๐Ÿ‘

Thread 1 Thread 2 Thread 3 counter = 42 buffer = "Thread 1" isReady = true counter = 17 buffer = "Thread 2" isReady = false counter = 99 buffer = "Thread 3" isReady = true ๊ฐ ์Šค๋ ˆ๋“œ๋ณ„ ๋…๋ฆฝ์ ์ธ TLS ๋ณ€์ˆ˜๋“ค

์œ„ ๊ทธ๋ฆผ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ๊ฐ™์€ ์ด๋ฆ„์˜ ๋ณ€์ˆ˜(counter, buffer, isReady)๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, ์‹ค์ œ ๊ฐ’์€ ๋ชจ๋‘ ๋‹ค๋ฅด์ฃ ! ์ด๊ฒŒ ๋ฐ”๋กœ TLS์˜ ๋งˆ๋ฒ•์ž…๋‹ˆ๋‹ค! โœจ

2. ์™œ TLS๊ฐ€ ํ•„์š”ํ•œ๊ฐ€์š”? ๐Ÿง

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๋‹ค ๋ณด๋ฉด ์ด๋Ÿฐ ์ƒํ™ฉ ๋งŽ์ด ๊ฒช์œผ์…จ์„ ๊ฑฐ์˜ˆ์š”:

  1. ์ „์—ญ ๋ณ€์ˆ˜ ์ ‘๊ทผ ์‹œ ๋ฝ(lock)์ด ํ•„์š”ํ•ด์„œ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋จ
  2. ์Šค๋ ˆ๋“œ๋งˆ๋‹ค ๋‹ค๋ฅธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š”๋ฐ ๋งค๋ฒˆ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ๋ฒˆ๊ฑฐ๋กœ์›€
  3. ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ(thread safety)์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง
  4. ์Šค๋ ˆ๋“œ๋ณ„ ์ž„์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์–ด๋ ค์›€

์ด๋Ÿฐ ๋ฌธ์ œ๋“ค์„ TLS๊ฐ€ ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•ด์ค˜์š”! ์˜ˆ๋ฅผ ๋“ค์–ด, ๋กœ๊น… ์‹œ์Šคํ…œ์—์„œ ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ์˜ ๋กœ๊ทธ ๋ฒ„ํผ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด ๋ฝ ์—†์ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ ๋กœ๊ทธ๋ฅผ ์Œ“์„ ์ˆ˜ ์žˆ์ฃ . ์š”์ฆ˜ ๊ฐ™์€ ๋ฉ€ํ‹ฐ์ฝ”์–ด ์‹œ๋Œ€์— ์„ฑ๋Šฅ ํ–ฅ์ƒ์— ์ง„์งœ ํฐ ๋„์›€์ด ๋œ๋‹ต๋‹ˆ๋‹ค! ๐Ÿš€

์žฌ๋Šฅ๋„ท์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ฐ•์˜๋ฅผ ์ฐพ์•„๋ณด๋ฉด, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ๊ฐ•์ขŒ๋“ค์ด ๋งŽ์ด ์žˆ์–ด์š”. ํŠนํžˆ TLS ๊ฐ™์€ ๊ณ ๊ธ‰ ๊ฐœ๋…์„ ๋ฐฐ์šฐ๋ฉด ๋” ํšจ์œจ์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์ฃ !

๐Ÿ” TLS๊ฐ€ ํ•„์š”ํ•œ ์‹ค์ œ ์‚ฌ๋ก€:

  1. ์›น ์„œ๋ฒ„์—์„œ ๊ฐ ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์Šค๋ ˆ๋“œ๋ณ„ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ
  2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ํ’€์—์„œ ์Šค๋ ˆ๋“œ๋ณ„ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ
  3. ๋žœ๋ค ๋„˜๋ฒ„ ์ƒ์„ฑ๊ธฐ์˜ ์Šค๋ ˆ๋“œ๋ณ„ ์‹œ๋“œ(seed) ๊ด€๋ฆฌ
  4. ํ”„๋กœํŒŒ์ผ๋ง/๋””๋ฒ„๊น… ๋„๊ตฌ์—์„œ ์Šค๋ ˆ๋“œ๋ณ„ ์ •๋ณด ์ˆ˜์ง‘
  5. ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์ž(allocator)์˜ ์Šค๋ ˆ๋“œ๋ณ„ ์บ์‹œ ๊ตฌํ˜„

3. C++์—์„œ TLS ๊ตฌํ˜„ ๋ฐฉ๋ฒ• ๐Ÿ’ป

C++์—์„œ TLS๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๊ฐ€ ์žˆ์–ด์š”:

3.1 C++11 thread_local ํ‚ค์›Œ๋“œ ์‚ฌ์šฉํ•˜๊ธฐ

C++11๋ถ€ํ„ฐ ๋„์ž…๋œ thread_local ํ‚ค์›Œ๋“œ๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ณ  ํ˜„๋Œ€์ ์ธ ๋ฐฉ๋ฒ•์ด์—์š”. ์ด ํ‚ค์›Œ๋“œ๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋Š” ๊ฐ ์Šค๋ ˆ๋“œ๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ๋ณต์‚ฌ๋ณธ์„ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

// ๊ธฐ๋ณธ์ ์ธ thread_local ์‚ฌ์šฉ ์˜ˆ์ œ
#include <iostream>
#include <thread>
#include <vector>

thread_local int counter = 0;  // ๊ฐ ์Šค๋ ˆ๋“œ๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ counter ๋ณ€์ˆ˜

void increment_counter(int id) {
    // ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ์ž์‹ ๋งŒ์˜ counter ๊ฐ’์„ ๊ฐ€์ง
    counter += id;
    std::cout << "Thread " << id << ": counter = " << counter << std::endl;
}

int main() {
    std::vector<std::thread> threads;
    
    // 5๊ฐœ์˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    for (int i = 1; i <= 5; i++) {
        threads.emplace_back(increment_counter, i);
    }
    
    // ๋ชจ๋“  ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ ๋Œ€๊ธฐ
    for (auto& t : threads) {
        t.join();
    }
    
    // ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์˜ counter ๊ฐ’ ์ถœ๋ ฅ
    std::cout << "Main thread: counter = " << counter << std::endl;
    
    return 0;
}

์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๊ฐ ์Šค๋ ˆ๋“œ๋งˆ๋‹ค ๋‹ค๋ฅธ counter ๊ฐ’์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์˜ counter๋Š” 0์ด๊ณ , ๋‚˜๋จธ์ง€ ์Šค๋ ˆ๋“œ๋“ค์€ ๊ฐ์ž ๋‹ค๋ฅธ ๊ฐ’์„ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ง„์งœ ์‹ ๊ธฐํ•˜์ฃ ? ใ…‹ใ…‹

3.2 ํ”Œ๋žซํผ๋ณ„ TLS ๊ตฌํ˜„ ๋ฐฉ์‹

C++11 ์ด์ „์ด๋‚˜ ๋” ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ํ”Œ๋žซํผ๋ณ„ TLS API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”:

Windows์—์„œ์˜ TLS ๊ตฌํ˜„

#include <windows.h>
#include <iostream>

// TLS ์ธ๋ฑ์Šค ์„ ์–ธ
DWORD tlsIndex;

void init_tls() {
    // TLS ์ธ๋ฑ์Šค ํ• ๋‹น
    tlsIndex = TlsAlloc();
    if (tlsIndex == TLS_OUT_OF_INDEXES) {
        std::cerr << "TlsAlloc failed!" << std::endl;
        exit(1);
    }
}

void cleanup_tls() {
    // TLS ์ธ๋ฑ์Šค ํ•ด์ œ
    TlsFree(tlsIndex);
}

void set_tls_value(int value) {
    // TLS ๊ฐ’ ์„ค์ •
    int* pValue = new int(value);
    if (!TlsSetValue(tlsIndex, pValue)) {
        std::cerr << "TlsSetValue failed!" << std::endl;
        delete pValue;
        exit(1);
    }
}

int get_tls_value() {
    // TLS ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
    int* pValue = static_cast<int*>(TlsGetValue(tlsIndex));
    if (pValue == nullptr) {
        return 0;
    }
    return *pValue;
}

void free_tls_value() {
    // TLS ๊ฐ’ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
    int* pValue = static_cast<int*>(TlsGetValue(tlsIndex));
    if (pValue != nullptr) {
        delete pValue;
        TlsSetValue(tlsIndex, nullptr);
    }
}

POSIX ์‹œ์Šคํ…œ(Linux, macOS ๋“ฑ)์—์„œ์˜ TLS ๊ตฌํ˜„

#include <pthread.h>
#include <iostream>

// TLS ํ‚ค ์„ ์–ธ
pthread_key_t tlsKey;

void destructor(void* value) {
    // ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ ์‹œ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ์†Œ๋ฉธ์ž
    delete static_cast<int*>(value);
}

void init_tls() {
    // TLS ํ‚ค ์ƒ์„ฑ
    if (pthread_key_create(&tlsKey, destructor) != 0) {
        std::cerr << "pthread_key_create failed!" << std::endl;
        exit(1);
    }
}

void cleanup_tls() {
    // TLS ํ‚ค ์‚ญ์ œ
    pthread_key_delete(tlsKey);
}

void set_tls_value(int value) {
    // TLS ๊ฐ’ ์„ค์ •
    int* pValue = new int(value);
    if (pthread_setspecific(tlsKey, pValue) != 0) {
        std::cerr << "pthread_setspecific failed!" << std::endl;
        delete pValue;
        exit(1);
    }
}

int get_tls_value() {
    // TLS ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
    int* pValue = static_cast<int*>(pthread_getspecific(tlsKey));
    if (pValue == nullptr) {
        return 0;
    }
    return *pValue;
}

์œ„ ์ฝ”๋“œ๋“ค์€ ์ข€ ๋ณต์žกํ•ด ๋ณด์ด์ฃ ? ใ…‹ใ…‹ใ…‹ ๊ทธ๋ž˜์„œ ๊ฐ€๋Šฅํ•˜๋ฉด thread_local ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ํ›จ์”ฌ ํŽธํ•ด์š”! ํ•˜์ง€๋งŒ ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ๋‚˜ ํŠน์ˆ˜ํ•œ ์ƒํ™ฉ์—์„œ๋Š” ์ด๋Ÿฐ ๋ฐฉ์‹๋„ ์•Œ์•„๋‘๋ฉด ์ข‹๋‹ต๋‹ˆ๋‹ค. ๐Ÿ˜‰

3.3 C++17/20์—์„œ์˜ TLS ๊ฐœ์„ ์‚ฌํ•ญ

C++17๊ณผ C++20์—์„œ๋Š” TLS ๊ด€๋ จ ๋ช‡ ๊ฐ€์ง€ ๊ฐœ์„ ์‚ฌํ•ญ์ด ์žˆ์–ด์š”:

  • โœ… ์ดˆ๊ธฐํ™” ์ˆœ์„œ ๋ณด์žฅ ๊ฐ•ํ™”
  • โœ… ์˜ˆ์™ธ ์•ˆ์ „์„ฑ ๊ฐœ์„ 
  • โœ… ์„ฑ๋Šฅ ์ตœ์ ํ™”
  • โœ… ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ปดํฌ๋„ŒํŠธ์™€์˜ ํ†ตํ•ฉ ๊ฐ•ํ™”

ํŠนํžˆ C++20์—์„œ๋Š” jthread์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ TLS ๋ณ€์ˆ˜์˜ ์ˆ˜๋ช… ๊ด€๋ฆฌ๊ฐ€ ๋” ์‰ฌ์›Œ์กŒ์–ด์š”!

#include <iostream>
#include <thread>

// C++20 ์˜ˆ์ œ
void example_cpp20() {
    thread_local struct TLSObject {
        int value = 0;
        
        TLSObject() { 
            std::cout << "TLSObject ์ƒ์„ฑ๋จ!" << std::endl; 
        }
        
        ~TLSObject() { 
            std::cout << "TLSObject ์†Œ๋ฉธ๋จ! ์ตœ์ข… ๊ฐ’: " << value << std::endl; 
        }
    } obj;
    
    std::jthread t1([](int id) {
        obj.value = id * 10;  // ์Šค๋ ˆ๋“œ๋ณ„ obj ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผ
        std::cout << "์Šค๋ ˆ๋“œ " << id << ": " << obj.value << std::endl;
    }, 1);
    
    std::jthread t2([](int id) {
        obj.value = id * 10;
        std::cout << "์Šค๋ ˆ๋“œ " << id << ": " << obj.value << std::endl;
    }, 2);
    
    // jthread๋Š” ์†Œ๋ฉธ์ž์—์„œ ์ž๋™์œผ๋กœ join() ํ˜ธ์ถœ
}

์ด ์ฝ”๋“œ์—์„œ๋Š” ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ๋งŒ์˜ TLSObject ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง€๋ฉฐ, ์Šค๋ ˆ๋“œ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ์ž๋™์œผ๋กœ ์†Œ๋ฉธ์ž๊ฐ€ ํ˜ธ์ถœ๋ผ์š”. ๋˜ํ•œ jthread๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ๊ฐ€ ๋” ๊ฐ„ํŽธํ•ด์กŒ๋‹ต๋‹ˆ๋‹ค! ๐Ÿ‘

4. TLS ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ โš ๏ธ

TLS๋Š” ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์ด ์žˆ์–ด์š”:

โš ๏ธ TLS ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ:

  1. ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€: ์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ๋งŽ์„์ˆ˜๋ก ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๋น„๋ก€ํ•ด์„œ ์ฆ๊ฐ€ํ•ด์š”.
  2. ์ดˆ๊ธฐํ™” ๋น„์šฉ: ๊ฐ ์Šค๋ ˆ๋“œ๋งˆ๋‹ค TLS ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™”๊ฐ€ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ๋ณต์žกํ•œ ๊ฐ์ฒด์˜ ๊ฒฝ์šฐ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์–ด์š”.
  3. DLL์—์„œ์˜ ์‚ฌ์šฉ: Windows์—์„œ DLL ๊ฐ„ TLS ๊ณต์œ  ์‹œ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ด์š”.
  4. ์Šค๋ ˆ๋“œ ํ’€ ํ™˜๊ฒฝ: ์Šค๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ ์‹œ TLS ๊ฐ’์ด ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์–ด์š”.
  5. ์ •์  ์ดˆ๊ธฐํ™” ์ˆœ์„œ: ์ „์—ญ TLS ๋ณ€์ˆ˜์˜ ์ดˆ๊ธฐํ™” ์ˆœ์„œ์— ์˜์กดํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”.

ํŠนํžˆ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์€ ์ง„์งœ ์ค‘์š”ํ•œ ํฌ์ธํŠธ์˜ˆ์š”! ์˜ˆ๋ฅผ ๋“ค์–ด, 1MB ํฌ๊ธฐ์˜ ๋ฒ„ํผ๋ฅผ thread_local๋กœ ์„ ์–ธํ•˜๊ณ  1000๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์•ฝ 1GB์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์‚ฌ์šฉ๋˜๋‹ˆ๊นŒ์š”! ๐Ÿ˜ฑ

๊ทธ๋ฆฌ๊ณ  TLS ๋ณ€์ˆ˜์— ํฌ์ธํ„ฐ๋‚˜ ์ฐธ์กฐ๋ฅผ ์ €์žฅํ•  ๋•Œ๋Š” ํŠนํžˆ ์ฃผ์˜ํ•ด์•ผ ํ•ด์š”. ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ์Šคํƒ์ด๋‚˜ ์ด๋ฏธ ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉด ์•ˆ ๋˜๊ฑฐ๋“ ์š”!

// ์œ„ํ—˜ํ•œ TLS ์‚ฌ์šฉ ์˜ˆ์ œ - ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š”!
thread_local int* dangerous_ptr;

void bad_example() {
    int local_var = 42;
    dangerous_ptr = &local_var;  // ๋กœ์ปฌ ๋ณ€์ˆ˜ ์ฃผ์†Œ๋ฅผ TLS์— ์ €์žฅ (์œ„ํ—˜!)
    
    std::thread t([]{
        // ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ dangerous_ptr ์‚ฌ์šฉ ์‹œ ์ด๋ฏธ ์†Œ๋ฉธ๋œ local_var ์ฐธ์กฐ
        std::cout << *dangerous_ptr << std::endl;  // ๋ฏธ์ •์˜ ๋™์ž‘!
    });
    
    t.detach();  // ์Šค๋ ˆ๋“œ ๋ถ„๋ฆฌ
    // ํ•จ์ˆ˜ ์ข…๋ฃŒ ์‹œ local_var ์†Œ๋ฉธ
}

์œ„ ์ฝ”๋“œ๋Š” ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋˜๋Š” ์˜ˆ์‹œ์˜ˆ์š”! ๋กœ์ปฌ ๋ณ€์ˆ˜์˜ ์ฃผ์†Œ๋ฅผ TLS์— ์ €์žฅํ•˜๊ณ  ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ฏธ์ •์˜ ๋™์ž‘(undefined behavior)์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์‹ค์ˆ˜ ์กฐ์‹ฌํ•˜์„ธ์š”! ๐Ÿ™…โ€โ™‚๏ธ

5. ์‹ค์ „ ์˜ˆ์ œ: TLS๋ฅผ ํ™œ์šฉํ•œ ๋กœ๊น… ์‹œ์Šคํ…œ ๐Ÿ”

์ด์ œ ์‹ค์ œ๋กœ ์œ ์šฉํ•œ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”! TLS๋ฅผ ํ™œ์šฉํ•œ ๊ณ ์„ฑ๋Šฅ ๋กœ๊น… ์‹œ์Šคํ…œ์ด์—์š”. ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ๋งŒ์˜ ๋กœ๊ทธ ๋ฒ„ํผ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ๋ฝ ์—†์ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์–ด์š”.

#include <iostream>
#include <fstream>
#include <thread>
#include <vector>
#include <string>
#include <mutex>
#include <chrono>

class ThreadSafeLogger {
private:
    static std::ofstream logFile;
    static std::mutex fileMutex;
    
    // ๊ฐ ์Šค๋ ˆ๋“œ๋ณ„ ๋กœ๊ทธ ๋ฒ„ํผ
    static thread_local std::vector<std::string> logBuffer;
    static thread_local int threadId;
    
    // ๋ฒ„ํผ ํฌ๊ธฐ ์ œํ•œ
    static constexpr size_t MAX_BUFFER_SIZE = 100;
    
public:
    static void init() {
        logFile.open("application.log", std::ios::app);
        if (!logFile) {
            std::cerr << "๋กœ๊ทธ ํŒŒ์ผ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!" << std::endl;
            exit(1);
        }
    }
    
    static void setThreadId(int id) {
        threadId = id;
    }
    
    static void log(const std::string& message) {
        // ํ˜„์žฌ ์‹œ๊ฐ„ ๊ฐ€์ ธ์˜ค๊ธฐ
        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);
        
        // ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ํ˜•์‹ํ™”
        std::string formattedMsg = "[Thread-" + std::to_string(threadId) + "] [" 
                                  + std::string(std::ctime(&time)).substr(0, 24) 
                                  + "] " + message;
        
        // ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ๋ฒ„ํผ์— ์ถ”๊ฐ€
        logBuffer.push_back(formattedMsg);
        
        // ๋ฒ„ํผ๊ฐ€ ๊ฐ€๋“ ์ฐจ๋ฉด ํ”Œ๋Ÿฌ์‹œ
        if (logBuffer.size() >= MAX_BUFFER_SIZE) {
            flush();
        }
    }
    
    static void flush() {
        if (logBuffer.empty()) {
            return;
        }
        
        // ํŒŒ์ผ ์“ฐ๊ธฐ๋Š” ๋ฝ์ด ํ•„์š”ํ•จ
        std::lock_guard<std::mutex> lock(fileMutex);
        
        for (const auto& msg : logBuffer) {
            logFile << msg << std::endl;
        }
        
        logFile.flush();
        logBuffer.clear();
    }
    
    static void shutdown() {
        // ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ์˜ ๋ฒ„ํผ๋ฅผ ํ”Œ๋Ÿฌ์‹œํ•ด์•ผ ํ•จ
        flush();
        
        // ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ๋งŒ ํŒŒ์ผ ๋‹ซ๊ธฐ
        static std::once_flag closeFlag;
        std::call_once(closeFlag, []() {
            if (logFile.is_open()) {
                logFile.close();
            }
        });
    }
};

// ์ •์  ๋ฉค๋ฒ„ ์ดˆ๊ธฐํ™”
std::ofstream ThreadSafeLogger::logFile;
std::mutex ThreadSafeLogger::fileMutex;
thread_local std::vector<std::string> ThreadSafeLogger::logBuffer;
thread_local int ThreadSafeLogger::threadId = 0;

// ์›Œ์ปค ์Šค๋ ˆ๋“œ ํ•จ์ˆ˜
void worker(int id) {
    ThreadSafeLogger::setThreadId(id);
    
    for (int i = 0; i < 1000; ++i) {
        ThreadSafeLogger::log("์ž‘์—… " + std::to_string(i) + " ์ฒ˜๋ฆฌ ์ค‘...");
        
        // ์‹ค์ œ ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
    
    ThreadSafeLogger::log("๋ชจ๋“  ์ž‘์—… ์™„๋ฃŒ!");
    ThreadSafeLogger::flush();
}

int main() {
    ThreadSafeLogger::init();
    
    std::vector<std::thread> threads;
    const int NUM_THREADS = 5;
    
    // ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    for (int i = 1; i <= NUM_THREADS; ++i) {
        threads.emplace_back(worker, i);
    }
    
    // ๋ชจ๋“  ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ ๋Œ€๊ธฐ
    for (auto& t : threads) {
        t.join();
    }
    
    ThreadSafeLogger::shutdown();
    std::cout << "๋กœ๊น… ์™„๋ฃŒ!" << std::endl;
    
    return 0;
}

์ด ๋กœ๊น… ์‹œ์Šคํ…œ์˜ ์žฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์š”:

  1. ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ๋งŒ์˜ ๋กœ๊ทธ ๋ฒ„ํผ๋ฅผ ๊ฐ€์ ธ ๋Œ€๋ถ€๋ถ„์˜ ๋กœ๊น… ์ž‘์—…์—์„œ ๋ฝ์ด ํ•„์š” ์—†์Œ
  2. ๋ฒ„ํผ๊ฐ€ ์ฐจ๊ฑฐ๋‚˜ ๋ช…์‹œ์ ์œผ๋กœ flush()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งŒ ๋ฝ์„ ์‚ฌ์šฉํ•ด ํŒŒ์ผ์— ๊ธฐ๋ก
  3. ๋กœ๊ทธ ๋ฉ”์‹œ์ง€์— ์Šค๋ ˆ๋“œ ID๊ฐ€ ์ž๋™์œผ๋กœ ํฌํ•จ๋˜์–ด ๋””๋ฒ„๊น…์ด ์‰ฌ์›€
  4. ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ ์‹œ ๋‚จ์€ ๋กœ๊ทธ๊ฐ€ ์ž๋™์œผ๋กœ ํ”Œ๋Ÿฌ์‹œ๋จ

์ด๋Ÿฐ ๋ฐฉ์‹์€ ๊ณ ์„ฑ๋Šฅ ์„œ๋ฒ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด์ด์—์š”. ๋กœ๊น… ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๋Š” ๋ฌธ์ œ๋ฅผ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”! ๐Ÿ˜Ž

TLS ๊ธฐ๋ฐ˜ ๋กœ๊น… ์‹œ์Šคํ…œ ์ž‘๋™ ๋ฐฉ์‹ Thread 1 ๋กœ๊ทธ ๋ฒ„ํผ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 1 ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 2 ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 3 ... ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ N Thread 2 ๋กœ๊ทธ ๋ฒ„ํผ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 1 ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 2 ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 3 ... ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ N Thread 3 ๋กœ๊ทธ ๋ฒ„ํผ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 1 ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 2 ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ 3 ... ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ N ๋กœ๊ทธ ํŒŒ์ผ Flush() Flush() Flush() ๐Ÿ”’

์œ„ ๋‹ค์ด์–ด๊ทธ๋žจ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ์ž์‹ ๋งŒ์˜ ๋กœ๊ทธ ๋ฒ„ํผ์— ๋ฉ”์‹œ์ง€๋ฅผ ์Œ“๋‹ค๊ฐ€ ๋ฒ„ํผ๊ฐ€ ๊ฐ€๋“ ์ฐจ๊ฑฐ๋‚˜ ๋ช…์‹œ์ ์œผ๋กœ flush()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งŒ ๋ฝ์„ ํš๋“ํ•˜๊ณ  ํŒŒ์ผ์— ๊ธฐ๋กํ•ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฝ ๊ฒฝํ•ฉ(lock contention)์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ‘

6. ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ ๐Ÿš€

TLS๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์„ฑ๋Šฅ์„ ์ตœ๋Œ€ํ•œ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•œ ํŒ๋“ค์„ ์•Œ์•„๋ณผ๊ฒŒ์š”:

6.1 TLS ๋ณ€์ˆ˜ ํฌ๊ธฐ ์ตœ์†Œํ™”

TLS ๋ณ€์ˆ˜์˜ ํฌ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์š”. ๊ฐ ์Šค๋ ˆ๋“œ๋งˆ๋‹ค ๋ณต์‚ฌ๋ณธ์ด ์ƒ์„ฑ๋˜๋ฏ€๋กœ ํฌ๊ธฐ๊ฐ€ ํด์ˆ˜๋ก ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•˜๊ณ  ์บ์‹œ ํšจ์œจ์„ฑ์ด ๋–จ์–ด์ ธ์š”.

// ๋‚˜์œ ์˜ˆ
thread_local std::array<double, 10000> hugeArray;  // ๊ฐ ์Šค๋ ˆ๋“œ๋งˆ๋‹ค 80KB ์‚ฌ์šฉ

// ์ข‹์€ ์˜ˆ
thread_local std::unique_ptr<std::array<double, 10000>> lazyArray;  // ํ•„์š”ํ•  ๋•Œ๋งŒ ํ• ๋‹น

void use_array() {
    if (!lazyArray) {
        lazyArray = std::make_unique<std::array<double, 10000>>();
    }
    // lazyArray ์‚ฌ์šฉ
}

6.2 ์ดˆ๊ธฐํ™” ๋น„์šฉ ์ค„์ด๊ธฐ

TLS ๋ณ€์ˆ˜๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์ฒ˜์Œ ์ ‘๊ทผํ•  ๋•Œ ์ดˆ๊ธฐํ™”๋ผ์š”. ๋ณต์žกํ•œ ์ดˆ๊ธฐํ™” ๋กœ์ง์ด ์žˆ๋‹ค๋ฉด ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์–ด์š”.

// ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ์ดˆ๊ธฐํ™”
thread_local std::map<std::string, std::vector<int>> expensiveMap = [](){
    std::map<std::string, std::vector<int>> m;
    // ๋ณต์žกํ•œ ์ดˆ๊ธฐํ™” ๋กœ์ง...
    return m;
}();

// ๊ฐœ์„ ๋œ ๋ฐฉ๋ฒ•: ์ง€์—ฐ ์ดˆ๊ธฐํ™” + ์บ์‹ฑ
std::map<std::string, std::vector<int>>& get_map() {
    thread_local std::map<std::string, std::vector<int>>* cache = nullptr;
    if (!cache) {
        static std::mutex initMutex;
        static std::map<std::string, std::vector<int>> masterCopy;
        
        {
            std::lock_guard<std::mutex> lock(initMutex);
            if (masterCopy.empty()) {
                // masterCopy ํ•œ ๋ฒˆ๋งŒ ์ดˆ๊ธฐํ™”
            }
        }
        
        // ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์บ์‹œ์— ๋ณต์‚ฌ
        thread_local std::map<std::string, std::vector<int>> localCopy = masterCopy;
        cache = &localCopy;
    }
    return *cache;
}

6.3 ์บ์‹œ ์ง€์—ญ์„ฑ ํ™œ์šฉ

TLS ๋ณ€์ˆ˜๋Š” ์Šค๋ ˆ๋“œ ์Šคํƒ ๊ทผ์ฒ˜์— ํ• ๋‹น๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„ ์บ์‹œ ์ง€์—ญ์„ฑ์ด ์ข‹์•„์š”. ์ด๋ฅผ ํ™œ์šฉํ•ด ์ž์ฃผ ์ ‘๊ทผํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ TLS์— ์บ์‹ฑํ•˜๋ฉด ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ์–ด์š”.