์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ C++๋กœ ๋ฐฐ์šฐ๋Š” ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ๊ณผ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๋ชจ๋“  ๊ฒƒ ๐Ÿš€

๐Ÿš€ C++๋กœ ๋ฐฐ์šฐ๋Š” ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ๊ณผ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๋ชจ๋“  ๊ฒƒ ๐Ÿš€

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

๐Ÿ“š ๋ชฉ์ฐจ

  1. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ๊ณผ ๋™์‹œ์„ฑ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…
  2. C++์—์„œ์˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ๊ณผ ๊ด€๋ฆฌ
  3. ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜: ๋ฎคํ…์Šค, ์„ธ๋งˆํฌ์–ด, ์กฐ๊ฑด ๋ณ€์ˆ˜
  4. ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค์™€ ๊ต์ฐฉ ์ƒํƒœ(Deadlock) ์ดํ•ดํ•˜๊ธฐ
  5. C++11/14/17/20์˜ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ๋“ค
  6. ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ Future/Promise
  7. ์Šค๋ ˆ๋“œ ํ’€๊ณผ ์ž‘์—… ํ ๊ตฌํ˜„ํ•˜๊ธฐ
  8. ์‹ค์ „ ์˜ˆ์ œ: ๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„
  9. ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ๊ณผ ๋””๋ฒ„๊น… ์ „๋žต
  10. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์˜ ๋ฏธ๋ž˜์™€ ์ตœ์‹  ํŠธ๋ Œ๋“œ

1. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ๊ณผ ๋™์‹œ์„ฑ์˜ ๊ธฐ๋ณธ ๊ฐœ๋… ๐Ÿงฉ

์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰๋  ๋•Œ, ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์ฒ˜๋ฆฌํ•ด. ์ด๊ฑธ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ ์‹คํ–‰์ด๋ผ๊ณ  ๋ถ€๋ฅด์ง€. ๊ทผ๋ฐ ์š”์ฆ˜ ์ปดํ“จํ„ฐ๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฝ”์–ด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ž–์•„? ์ด ๋ชจ๋“  ์ฝ”์–ด๋ฅผ ํ™œ์šฉํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ? ๋ฐ”๋กœ ์—ฌ๊ธฐ์„œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์ด ๋“ฑ์žฅํ•˜๋Š” ๊ฑฐ์•ผ! ๐ŸŽฎ

์Šค๋ ˆ๋“œ(Thread)๋ž€ ๋ญ˜๊นŒ? ๐Ÿค”

์Šค๋ ˆ๋“œ๋Š” ํ”„๋กœ์„ธ์Šค ๋‚ด์—์„œ ์‹คํ–‰๋˜๋Š” ์ž‘์—…์˜ ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„์•ผ. ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ , ์ด ์Šค๋ ˆ๋“œ๋“ค์€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ๊ณต์œ ํ•˜๋ฉด์„œ ๋™์‹œ์— ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด.

ํ”„๋กœ์„ธ์Šค (Process) ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ ์ฝ”๋“œ (Code) ๋ฐ์ดํ„ฐ (Data) ํž™ (Heap) ์Šค๋ ˆ๋“œ 1 ์Šค๋ ˆ๋“œ 2 ...

๋™์‹œ์„ฑ(Concurrency)๊ณผ ๋ณ‘๋ ฌ์„ฑ(Parallelism)์˜ ์ฐจ์ด ๐Ÿ”„

๋™์‹œ์„ฑ (Concurrency)

๋™์‹œ์„ฑ์€ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•ด. ์‹ค์ œ๋กœ๋Š” ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ, ๋น ๋ฅด๊ฒŒ ์ „ํ™˜ํ•˜๋ฉด์„œ ๋งˆ์น˜ ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ๊ฑฐ์ง€. ์‹ฑ๊ธ€ ์ฝ”์–ด์—์„œ๋„ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•ด!

๋ณ‘๋ ฌ์„ฑ (Parallelism)

๋ณ‘๋ ฌ์„ฑ์€ ์‹ค์ œ๋กœ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ด. ์ด๊ฑด ๋ฉ€ํ‹ฐ ์ฝ”์–ด ํ™˜๊ฒฝ์—์„œ๋งŒ ์ง„์ •ํ•œ ์˜๋ฏธ์˜ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜์ง€. ๊ฐ ์ฝ”์–ด๊ฐ€ ๋…๋ฆฝ์ ์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋‹ˆ๊นŒ!

์žฌ๋Šฅ๋„ท์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ฐ•์˜๋ฅผ ์ฐพ์•„๋ณด๋ฉด, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ๊ณผ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋Œ€ํ•œ ๋‹ค์–‘ํ•œ ํŠœํ† ๋ฆฌ์–ผ์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ํŠนํžˆ C++ ์ „๋ฌธ๊ฐ€๋“ค์ด ์‹ค๋ฌด์—์„œ ์–ด๋–ป๊ฒŒ ์ด๋Ÿฐ ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜๋Š”์ง€ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์ง€! ๐Ÿ˜‰

2. C++์—์„œ์˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ๊ณผ ๊ด€๋ฆฌ ๐Ÿงต

C++11๋ถ€ํ„ฐ๋Š” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— std::thread๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ํ›จ์”ฌ ์‰ฌ์›Œ์กŒ์–ด. ์ด์ „์—๋Š” ์šด์˜์ฒด์ œ๋ณ„๋กœ ๋‹ค๋ฅธ API๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์ง€๋งŒ, ์ด์ œ๋Š” ํ‘œ์ค€ํ™”๋œ ๋ฐฉ๋ฒ•์œผ๋กœ ์Šค๋ ˆ๋“œ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฑฐ์ง€! ๐Ÿ‘

์ฒซ ๋ฒˆ์งธ ์Šค๋ ˆ๋“œ ๋งŒ๋“ค๊ธฐ ๐Ÿ

C++์—์„œ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋ณธ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊นŒ?

#include <iostream>
#include <thread>

void hello_thread() {
    std::cout << "์•ˆ๋…•! ๋‚˜๋Š” ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ์•ผ!" << std::endl;
}

int main() {
    // ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    std::thread t(hello_thread);
    
    // ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ถœ๋ ฅ
    std::cout << "๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ธ์‚ฌํ• ๊ฒŒ~" << std::endl;
    
    // ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ ๋Œ€๊ธฐ
    t.join();
    
    return 0;
}

์œ„ ์ฝ”๋“œ์—์„œ std::thread t(hello_thread)๋Š” ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , hello_thread ํ•จ์ˆ˜๋ฅผ ๊ทธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•ด. ๊ทธ๋ฆฌ๊ณ  t.join()์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ t ์Šค๋ ˆ๋“œ๊ฐ€ ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋„๋ก ํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋จผ์ € ์ข…๋ฃŒ๋˜์–ด ํ”„๋กœ๊ทธ๋žจ์ด ๋น„์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋  ์ˆ˜ ์žˆ์–ด! โš ๏ธ

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ

์Šค๋ ˆ๋“œ ๊ฐ์ฒด๊ฐ€ ์†Œ๋ฉธ๋  ๋•Œ๊นŒ์ง€ join() ๋˜๋Š” detach()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด, std::terminate๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ํ”„๋กœ๊ทธ๋žจ์ด ๊ฐ•์ œ ์ข…๋ฃŒ๋ผ. ํ•ญ์ƒ ์Šค๋ ˆ๋“œ๋ฅผ ์ ์ ˆํžˆ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ์Šต๊ด€์„ ๋“ค์ด์ž!

์Šค๋ ˆ๋“œ์— ์ธ์ž ์ „๋‹ฌํ•˜๊ธฐ ๐Ÿ“ฆ

์Šค๋ ˆ๋“œ ํ•จ์ˆ˜์— ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๋„ ๋งค์šฐ ๊ฐ„๋‹จํ•ด:

#include <iostream>
#include <thread>
#include <string>

void greeting(std::string name, int age) {
    std::cout << "์•ˆ๋…•, " << name << "! ๋„ˆ์˜ ๋‚˜์ด๋Š” " << age << "์‚ด์ด๊ตฌ๋‚˜!" << std::endl;
}

int main() {
    std::string name = "์ฝ”๋”ฉ์ดˆ๋ณด";
    int age = 25;
    
    // ์ธ์ž์™€ ํ•จ๊ป˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    std::thread t(greeting, name, age);
    
    t.join();
    return 0;
}
๋ฉ”์ธ ์Šค๋ ˆ๋“œ ์ž์‹ ์Šค๋ ˆ๋“œ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ join() ์™„๋ฃŒ name, age

๋žŒ๋‹ค ํ•จ์ˆ˜๋กœ ์Šค๋ ˆ๋“œ ์ƒ์„ฑํ•˜๊ธฐ ๐ŸŽฏ

C++11์˜ ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด:

#include <iostream>
#include <thread>

int main() {
    int number = 42;
    
    // ๋žŒ๋‹ค ํ•จ์ˆ˜๋กœ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    std::thread t([number]() {
        std::cout << "๋žŒ๋‹ค ์Šค๋ ˆ๋“œ์—์„œ number ๊ฐ’: " << number << std::endl;
    });
    
    t.join();
    return 0;
}

๋žŒ๋‹ค ํ•จ์ˆ˜๋Š” ๊ฐ„๊ฒฐํ•˜๊ณ  ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์„œ ์ž‘์€ ์ž‘์—…์„ ์Šค๋ ˆ๋“œ๋กœ ์‹คํ–‰ํ•  ๋•Œ ํŠนํžˆ ์œ ์šฉํ•ด. ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ [number]๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€ ๋ณ€์ˆ˜๋ฅผ ์Šค๋ ˆ๋“œ ๋‚ด๋ถ€๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์ง€! ๐Ÿ”„

์Šค๋ ˆ๋“œ ๊ด€๋ฆฌํ•˜๊ธฐ: join vs detach ๐Ÿ”€

join()

join()์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ํ˜ธ์ถœ ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋ก์‹œ์ผœ. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ์Šค๋ ˆ๋“œ์˜ ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•˜๋Š” ๊ฑฐ์ง€. ์ž‘์—…์˜ ๊ฒฐ๊ณผ๊ฐ€ ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ์ˆœ์ฐจ์  ์‹คํ–‰์ด ์ค‘์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•ด.

detach()

detach()๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋ถ„๋ฆฌํ•ด. ๋ถ„๋ฆฌ๋œ ์Šค๋ ˆ๋“œ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉฐ, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๊ณ„์† ์ง„ํ–‰๋ผ. ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์ด๋‚˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•ด.

#include <iostream>
#include <thread>
#include <chrono>

void background_task() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… ์™„๋ฃŒ!" << std::endl;
}

int main() {
    // detach ์˜ˆ์ œ
    std::thread t(background_task);
    t.detach();
    
    std::cout << "๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๊ณ„์† ์‹คํ–‰๋ผ!" << std::endl;
    
    // ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋นจ๋ฆฌ ์ข…๋ฃŒ๋˜์ง€ ์•Š๋„๋ก ์ž ์‹œ ๋Œ€๊ธฐ
    std::this_thread::sleep_for(std::chrono::seconds(3));
    
    return 0;
}

์ฃผ์˜! detach๋œ ์Šค๋ ˆ๋“œ๋Š” ์ œ์–ดํ•  ์ˆ˜ ์—†๊ฒŒ ๋˜๋ฏ€๋กœ, ๋ฉ”์ธ ํ”„๋กœ๊ทธ๋žจ์ด ์ข…๋ฃŒ๋˜๋ฉด ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋„ ๊ฐ•์ œ ์ข…๋ฃŒ๋  ์ˆ˜ ์žˆ์–ด. ์ค‘์š”ํ•œ ์ž‘์—…์ด๋ผ๋ฉด join()์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ด! ๐Ÿ›ก๏ธ

3. ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜: ๋ฎคํ…์Šค, ์„ธ๋งˆํฌ์–ด, ์กฐ๊ฑด ๋ณ€์ˆ˜ ๐Ÿ”’

์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋ฉด ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค(Data Race)๋ผ๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. C++์—์„œ๋Š” ๋‹ค์–‘ํ•œ ๋™๊ธฐํ™” ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์ง€! ๐Ÿ”ง

๋ฎคํ…์Šค(Mutex): ์ƒํ˜ธ ๋ฐฐ์ œ ๋ฝ ๐Ÿ”

๋ฎคํ…์Šค๋Š” Mutual Exclusion์˜ ์ค„์ž„๋ง๋กœ, ๊ณต์œ  ์ž์›์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋กœ ์ œํ•œํ•˜๋Š” ๋™๊ธฐํ™” ๋„๊ตฌ์•ผ.

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>

std::mutex mtx;  // ๋ฎคํ…์Šค ๊ฐ์ฒด ์ƒ์„ฑ
int shared_value = 0;

void increment_value(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        mtx.lock();  // ๋ฝ ํš๋“
        shared_value++;  // ์ž„๊ณ„ ์˜์—ญ(Critical Section)
        mtx.unlock();  // ๋ฝ ํ•ด์ œ
    }
}

int main() {
    const int iterations = 1000000;
    
    std::thread t1(increment_value, iterations);
    std::thread t2(increment_value, iterations);
    
    t1.join();
    t2.join();
    
    std::cout << "์ตœ์ข… ๊ฐ’: " << shared_value << std::endl;
    // ๋ฎคํ…์Šค๊ฐ€ ์—†๋‹ค๋ฉด 2000000๋ณด๋‹ค ์ž‘์€ ๊ฐ’์ด ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์Œ!
    
    return 0;
}

์œ„ ์ฝ”๋“œ์—์„œ mtx.lock()๊ณผ mtx.unlock() ์‚ฌ์ด์˜ ์ฝ”๋“œ๋Š” ์ž„๊ณ„ ์˜์—ญ(Critical Section)์ด๋ผ๊ณ  ๋ถˆ๋Ÿฌ. ์ด ์˜์—ญ์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ˜Œ

๋ฝ ๊ฐ€๋“œ(Lock Guard): ๋” ์•ˆ์ „ํ•œ ๋ฎคํ…์Šค ์‚ฌ์šฉ๋ฒ• ๐Ÿ›ก๏ธ

์ง์ ‘ lock/unlock์„ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ์‹ค์ˆ˜ํ•˜๊ธฐ ์‰ฌ์›Œ. C++์—์„œ๋Š” RAII(Resource Acquisition Is Initialization) ํŒจํ„ด์„ ํ™œ์šฉํ•œ std::lock_guard๋ฅผ ์ œ๊ณตํ•ด:

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>

std::mutex mtx;
int shared_value = 0;

void increment_value_safe(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        // ์Šค์ฝ”ํ”„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์ž๋™์œผ๋กœ unlock ๋จ
        std::lock_guard<std::mutex> lock(mtx);
        shared_value++;
    }
}

int main() {
    const int iterations = 1000000;
    
    std::thread t1(increment_value_safe, iterations);
    std::thread t2(increment_value_safe, iterations);
    
    t1.join();
    t2.join();
    
    std::cout << "์ตœ์ข… ๊ฐ’: " << shared_value << std::endl;
    
    return 0;
}

std::lock_guard๋Š” ์ƒ์„ฑ๋  ๋•Œ ๋ฎคํ…์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ž ๊ทธ๊ณ , ์†Œ๋ฉธ๋  ๋•Œ ์ž๋™์œผ๋กœ ํ•ด์ œํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ๋ฎคํ…์Šค๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ํ•ด์ œ๋˜๋ฏ€๋กœ ๋ฐ๋“œ๋ฝ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ‘

๊ณต์œ  ์ž์› ๐Ÿ”’ ์Šค๋ ˆ๋“œ 1 ์Šค๋ ˆ๋“œ 2 ์Šค๋ ˆ๋“œ 3 ์Šค๋ ˆ๋“œ 4 ์ ‘๊ทผ ์‹œ๋„ ๋Œ€๊ธฐ ์ค‘ ๋Œ€๊ธฐ ์ค‘ ๋Œ€๊ธฐ ์ค‘ โœ“ ๋ฝ ๋ณด์œ  ์ค‘ โœ— ๋Œ€๊ธฐ ์ค‘ โœ— ๋Œ€๊ธฐ ์ค‘ โœ— ๋Œ€๊ธฐ ์ค‘

์กฐ๊ฑด ๋ณ€์ˆ˜(Condition Variable): ์Šค๋ ˆ๋“œ ๊ฐ„ ์‹ ํ˜ธ ์ „๋‹ฌ ๐Ÿ“ข

์กฐ๊ฑด ๋ณ€์ˆ˜๋Š” ์Šค๋ ˆ๋“œ ๊ฐ„์— ์‹ ํ˜ธ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ด. ํŠน์ • ์กฐ๊ฑด์ด ์ถฉ์กฑ๋  ๋•Œ๊นŒ์ง€ ์Šค๋ ˆ๋“œ๋ฅผ ๋Œ€๊ธฐ์‹œํ‚ค๊ณ , ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜๋ฉด ๋Œ€๊ธฐ ์ค‘์ธ ์Šค๋ ˆ๋“œ์—๊ฒŒ ์•Œ๋ ค์ฃผ๋Š” ๊ฑฐ์ง€.

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool done = false;

void producer() {
    for (int i = 0; i < 10; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            data_queue.push(i);
            std::cout << "์ƒ์‚ฐ: " << i << std::endl;
        }
        cv.notify_one();  // ๋Œ€๊ธฐ ์ค‘์ธ ์†Œ๋น„์ž์—๊ฒŒ ์‹ ํ˜ธ ์ „๋‹ฌ
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    {
        std::lock_guard<std::mutex> lock(mtx);
        done = true;
    }
    cv.notify_one();  // ์ž‘์—… ์™„๋ฃŒ ์‹ ํ˜ธ
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return !data_queue.empty() || done; });
        
        if (data_queue.empty() && done) {
            break;  // ์ž‘์—… ์™„๋ฃŒ
        }
        
        int value = data_queue.front();
        data_queue.pop();
        std::cout << "์†Œ๋น„: " << value << std::endl;
        
        lock.unlock();  // ์ฒ˜๋ฆฌ ์ค‘์—๋Š” ๋ฝ ํ•ด์ œ
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

int main() {
    std::thread prod(producer);
    std::thread cons(consumer);
    
    prod.join();
    cons.join();
    
    return 0;
}

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

์„ธ๋งˆํฌ์–ด(Semaphore): ๋ฆฌ์†Œ์Šค ์นด์šดํŒ… ๐Ÿงฎ

C++20๋ถ€ํ„ฐ๋Š” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— std::counting_semaphore์™€ std::binary_semaphore๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์–ด. ์„ธ๋งˆํฌ์–ด๋Š” ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์†Œ์Šค์˜ ์ˆ˜๋ฅผ ์ œํ•œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ผ.

#include <iostream>
#include <thread>
#include <semaphore>  // C++20
#include <vector>
#include <chrono>

// ์ตœ๋Œ€ 3๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ๊ฐ€๋Šฅ
std::counting_semaphore<3> sem(3);

void worker(int id) {
    std::cout << "์ž‘์—…์ž " << id << " ๋Œ€๊ธฐ ์ค‘..." << std::endl;
    
    sem.acquire();  // ์„ธ๋งˆํฌ์–ด ํš๋“ (์นด์šดํŠธ ๊ฐ์†Œ)
    std::cout << "์ž‘์—…์ž " << id << " ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ์‹œ์ž‘" << std::endl;
    
    // ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    std::cout << "์ž‘์—…์ž " << id << " ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ์™„๋ฃŒ" << std::endl;
    sem.release();  // ์„ธ๋งˆํฌ์–ด ํ•ด์ œ (์นด์šดํŠธ ์ฆ๊ฐ€)
}

int main() {
    std::vector<std::thread> threads;
    
    // 5๊ฐœ์˜ ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(worker, i);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    return 0;
}

์œ„ ์˜ˆ์ œ์—์„œ๋Š” ์ตœ๋Œ€ 3๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋งŒ ๋™์‹œ์— ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด. ๋‚˜๋จธ์ง€ ์Šค๋ ˆ๋“œ๋Š” ์„ธ๋งˆํฌ์–ด๊ฐ€ ํ•ด์ œ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•˜๊ฒŒ ๋ผ. ์ด๊ฑด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ํ’€์ด๋‚˜ ์Šค๋ ˆ๋“œ ํ’€ ๊ฐ™์€ ์ œํ•œ๋œ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ ์œ ์šฉํ•ด! ๐ŸŒŸ

4. ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค์™€ ๊ต์ฐฉ ์ƒํƒœ(Deadlock) ์ดํ•ดํ•˜๊ธฐ โš ๏ธ

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ๊ฐ€์žฅ ํ”ํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ๋Š” ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค์™€ ๊ต์ฐฉ ์ƒํƒœ(Deadlock)์•ผ. ์ด ๋ฌธ์ œ๋“ค์„ ์ดํ•ดํ•˜๊ณ  ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ”

๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค(Data Race) ๐ŸŽ๏ธ

๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค๋Š” ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ์œ„์น˜์— ์ ‘๊ทผํ•˜๊ณ , ๊ทธ ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์ด ์“ฐ๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ๋ฐœ์ƒํ•ด. ์ด๋กœ ์ธํ•ด ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์–ด!

// ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค ์˜ˆ์‹œ
#include <iostream>
#include <thread>
#include <vector>

int counter = 0;

void increment() {
    for (int i = 0; i < 1000000; ++i) {
        // ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค ๋ฐœ์ƒ!
        counter++;  // ์ฝ๊ธฐ-์ˆ˜์ •-์“ฐ๊ธฐ ์—ฐ์‚ฐ์ด ์›์ž์ ์ด์ง€ ์•Š์Œ
    }
}

int main() {
    std::vector<std::thread> threads;
    
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "์˜ˆ์ƒ ๊ฒฐ๊ณผ: " << 10 * 1000000 << std::endl;
    std::cout << "์‹ค์ œ ๊ฒฐ๊ณผ: " << counter << std::endl;  // ์˜ˆ์ƒ๋ณด๋‹ค ์ž‘์€ ๊ฐ’
    
    return 0;
}

์œ„ ์ฝ”๋“œ์—์„œ counter++ ์—ฐ์‚ฐ์€ ์›์ž์ (atomic)์ด์ง€ ์•Š์•„. ์ฆ‰, ์ฝ๊ธฐ-์ˆ˜์ •-์“ฐ๊ธฐ ๋‹จ๊ณ„๋กœ ๋‚˜๋‰˜๋Š”๋ฐ, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ผ๋ถ€ ์ฆ๊ฐ€ ์—ฐ์‚ฐ์ด ์†์‹ค๋  ์ˆ˜ ์žˆ์–ด. ๐Ÿ˜ฑ

๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ๐Ÿ’ก

  1. ๋ฎคํ…์Šค ์‚ฌ์šฉ: ๊ณต์œ  ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์‹œ ๋ฎคํ…์Šค๋กœ ๋ณดํ˜ธ
  2. ์›์ž์  ์—ฐ์‚ฐ ์‚ฌ์šฉ: std::atomic ํƒ€์ž… ํ™œ์šฉ
  3. ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ์ €์žฅ์†Œ: ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ๋…๋ฆฝ์ ์ธ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
  4. ๋ฝ ์—†๋Š” ์ž๋ฃŒ๊ตฌ์กฐ: ํŠน๋ณ„ํžˆ ์„ค๊ณ„๋œ ๋™์‹œ์„ฑ ์ž๋ฃŒ๊ตฌ์กฐ ํ™œ์šฉ

์›์ž์  ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•œ ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณผ๊นŒ?

#include <iostream>
#include <thread>
#include <vector>
#include <atomic>

std::atomic<int> atomic_counter(0);  // ์›์ž์  ์นด์šดํ„ฐ

void increment_atomic() {
    for (int i = 0; i < 1000000; ++i) {
        atomic_counter++;  // ์›์ž์  ์—ฐ์‚ฐ, ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค ์—†์Œ
    }
}

int main() {
    std::vector<std::thread> threads;
    
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment_atomic);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "์˜ˆ์ƒ ๊ฒฐ๊ณผ: " << 10 * 1000000 << std::endl;
    std::cout << "์‹ค์ œ ๊ฒฐ๊ณผ: " << atomic_counter << std::endl;  // ์˜ˆ์ƒ๊ณผ ์ผ์น˜!
    
    return 0;
}

std::atomic์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฝ ์—†์ด๋„ ์›์ž์  ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด. ์ด๋Š” ๊ฐ„๋‹จํ•œ ์นด์šดํ„ฐ๋‚˜ ํ”Œ๋ž˜๊ทธ์— ๋งค์šฐ ํšจ์œจ์ ์ด์•ผ! ๐Ÿš€

๊ต์ฐฉ ์ƒํƒœ(Deadlock) ๐Ÿ’€

๊ต์ฐฉ ์ƒํƒœ๋Š” ๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ์„œ๋กœ๊ฐ€ ๋ณด์œ ํ•œ ์ž์›์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ์˜์›ํžˆ ๋ธ”๋ก๋œ ์ƒํƒœ๋ฅผ ๋งํ•ด. ๋งˆ์น˜ ๋‘ ์‚ฌ๋žŒ์ด ์„œ๋กœ ์ƒ๋Œ€๋ฐฉ์ด ๋จผ์ € ๋น„์ผœ์ฃผ๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ง€!

// ๊ต์ฐฉ ์ƒํƒœ ์˜ˆ์‹œ
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1, mutex2;

void thread_a() {
    std::cout << "์Šค๋ ˆ๋“œ A ์‹œ์ž‘" << std::endl;
    
    mutex1.lock();  // ์ฒซ ๋ฒˆ์งธ ๋ฎคํ…์Šค ์ž ๊ธˆ
    std::cout << "์Šค๋ ˆ๋“œ A: mutex1 ํš๋“" << std::endl;
    
    std::this_thread::sleep_for(std::chrono::milliseconds(100));  // ๊ต์ฐฉ ์ƒํƒœ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ ๋†’์ด๊ธฐ
    
    mutex2.lock();  // ๋‘ ๋ฒˆ์งธ ๋ฎคํ…์Šค ์ž ๊ธˆ ์‹œ๋„ (์—ฌ๊ธฐ์„œ ๊ต์ฐฉ ์ƒํƒœ ๋ฐœ์ƒ ๊ฐ€๋Šฅ)
    std::cout << "์Šค๋ ˆ๋“œ A: mutex2 ํš๋“" << std::endl;
    
    // ์ž‘์—… ์ˆ˜ํ–‰...
    
    mutex2.unlock();
    mutex1.unlock();
}

void thread_b() {
    std::cout << "์Šค๋ ˆ๋“œ B ์‹œ์ž‘" << std::endl;
    
    mutex2.lock();  // ๋‘ ๋ฒˆ์งธ ๋ฎคํ…์Šค ์ž ๊ธˆ
    std::cout << "์Šค๋ ˆ๋“œ B: mutex2 ํš๋“" << std::endl;
    
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    mutex1.lock();  // ์ฒซ ๋ฒˆ์งธ ๋ฎคํ…์Šค ์ž ๊ธˆ ์‹œ๋„ (์—ฌ๊ธฐ์„œ ๊ต์ฐฉ ์ƒํƒœ ๋ฐœ์ƒ ๊ฐ€๋Šฅ)
    std::cout << "์Šค๋ ˆ๋“œ B: mutex1 ํš๋“" << std::endl;
    
    // ์ž‘์—… ์ˆ˜ํ–‰...
    
    mutex1.unlock();
    mutex2.unlock();
}

int main() {
    std::thread t1(thread_a);
    std::thread t2(thread_b);
    
    t1.join();
    t2.join();
    
    std::cout << "ํ”„๋กœ๊ทธ๋žจ ์ •์ƒ ์ข…๋ฃŒ" << std::endl;  // ๊ต์ฐฉ ์ƒํƒœ ๋ฐœ์ƒ ์‹œ ์ด ์ค„์€ ์‹คํ–‰๋˜์ง€ ์•Š์Œ
    
    return 0;
}

์œ„ ์ฝ”๋“œ์—์„œ ์Šค๋ ˆ๋“œ A๋Š” mutex1์„ ๋จผ์ € ์ž ๊ทธ๊ณ  mutex2๋ฅผ ์ž ๊ทธ๋ ค ํ•˜๊ณ , ์Šค๋ ˆ๋“œ B๋Š” mutex2๋ฅผ ๋จผ์ € ์ž ๊ทธ๊ณ  mutex1์„ ์ž ๊ทธ๋ ค ํ•ด. ์ด๋กœ ์ธํ•ด ์„œ๋กœ๊ฐ€ ์„œ๋กœ์˜ ๋ฎคํ…์Šค๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ต์ฐฉ ์ƒํƒœ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ˜ฑ

์Šค๋ ˆ๋“œ A ์Šค๋ ˆ๋“œ B Mutex 1 Mutex 2 ๋ณด์œ  ์ค‘ ๋Œ€๊ธฐ ์ค‘ ๋ณด์œ  ์ค‘ ๋Œ€๊ธฐ ์ค‘ ๊ต์ฐฉ ์ƒํƒœ!

๊ต์ฐฉ ์ƒํƒœ ๋ฐฉ์ง€ ๋ฐฉ๋ฒ• ๐Ÿ’ก

  1. ๋ฝ ์ˆœ์„œ ์ผ๊ด€์„ฑ ์œ ์ง€: ํ•ญ์ƒ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋ฎคํ…์Šค ์ž ๊ธˆ
  2. std::lock ์‚ฌ์šฉ: ์—ฌ๋Ÿฌ ๋ฎคํ…์Šค๋ฅผ ํ•œ ๋ฒˆ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ž ๊ธˆ
  3. ๋ฝ ํƒ€์ž„์•„์›ƒ ์„ค์ •: ์ผ์ • ์‹œ๊ฐ„ ํ›„ ๋ฝ ํš๋“ ํฌ๊ธฐ
  4. ๊ต์ฐฉ ์ƒํƒœ ๊ฐ์ง€: ๋””๋ฒ„๊น… ๋„๊ตฌ๋กœ ๊ต์ฐฉ ์ƒํƒœ ๊ฐ์ง€

std::lock์„ ์‚ฌ์šฉํ•œ ์•ˆ์ „ํ•œ ๋‹ค์ค‘ ๋ฝ ์˜ˆ์‹œ:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1, mutex2;

void safe_thread_a() {
    std::cout << "์•ˆ์ „ํ•œ ์Šค๋ ˆ๋“œ A ์‹œ์ž‘" << std::endl;
    
    std::lock(mutex1, mutex2);  // ๋‘ ๋ฎคํ…์Šค๋ฅผ ๋™์‹œ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ž ๊ธˆ
    std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
    
    std::cout << "์•ˆ์ „ํ•œ ์Šค๋ ˆ๋“œ A: ๋‘ ๋ฎคํ…์Šค ๋ชจ๋‘ ํš๋“" << std::endl;
    
    // ์ž‘์—… ์ˆ˜ํ–‰...
    
    // lock_guard์˜ ์†Œ๋ฉธ์ž๊ฐ€ ์ž๋™์œผ๋กœ ๋ฎคํ…์Šค ํ•ด์ œ
}

void safe_thread_b() {
    std::cout << "์•ˆ์ „ํ•œ ์Šค๋ ˆ๋“œ B ์‹œ์ž‘" << std::endl;
    
    std::lock(mutex1, mutex2);  // ๋‘ ๋ฎคํ…์Šค๋ฅผ ๋™์‹œ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ž ๊ธˆ
    std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
    
    std::cout << "์•ˆ์ „ํ•œ ์Šค๋ ˆ๋“œ B: ๋‘ ๋ฎคํ…์Šค ๋ชจ๋‘ ํš๋“" << std::endl;
    
    // ์ž‘์—… ์ˆ˜ํ–‰...
    
    // lock_guard์˜ ์†Œ๋ฉธ์ž๊ฐ€ ์ž๋™์œผ๋กœ ๋ฎคํ…์Šค ํ•ด์ œ
}

int main() {
    std::thread t1(safe_thread_a);
    std::thread t2(safe_thread_b);
    
    t1.join();
    t2.join();
    
    std::cout << "ํ”„๋กœ๊ทธ๋žจ ์ •์ƒ ์ข…๋ฃŒ" << std::endl;
    
    return 0;
}

std::lock์€ ๊ต์ฐฉ ์ƒํƒœ ์—†์ด ์—ฌ๋Ÿฌ ๋ฎคํ…์Šค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ž ๊ทธ๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ด. C++17๋ถ€ํ„ฐ๋Š” ๋” ๊ฐ„๋‹จํ•œ std::scoped_lock๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ‘

// C++17์˜ scoped_lock ์‚ฌ์šฉ ์˜ˆ
std::scoped_lock lock(mutex1, mutex2);  // ๋” ๊ฐ„๊ฒฐํ•œ ๋ฌธ๋ฒ•!

5. C++11/14/17/20์˜ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ๋“ค ๐Ÿ”„

C++์€ ๋ฒ„์ „์ด ์˜ฌ๋ผ๊ฐˆ์ˆ˜๋ก ๋” ๊ฐ•๋ ฅํ•œ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ๋“ค์„ ์ถ”๊ฐ€ํ•ด์™”์–ด. ๊ฐ ๋ฒ„์ „๋ณ„๋กœ ์–ด๋–ค ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž! ๐Ÿ“ˆ

C++11์˜ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ ๐Ÿš€

C++11์€ ํ‘œ์ค€ ์Šค๋ ˆ๋”ฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฒ˜์Œ์œผ๋กœ ๋„์ž…ํ–ˆ์–ด. ์ด์ „์—๋Š” POSIX ์Šค๋ ˆ๋“œ๋‚˜ Windows ์Šค๋ ˆ๋“œ ๊ฐ™์€ ํ”Œ๋žซํผ๋ณ„ API๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์ง€!

std::thread

์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค. ํ•จ์ˆ˜, ํ•จ์ˆ˜ ๊ฐ์ฒด, ๋žŒ๋‹ค ํ‘œํ˜„์‹ ๋“ฑ์„ ์Šค๋ ˆ๋“œ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด.

std::mutex, std::recursive_mutex

์ƒํ˜ธ ๋ฐฐ์ œ ๋ฝ์„ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค. recursive_mutex๋Š” ๊ฐ™์€ ์Šค๋ ˆ๋“œ์—์„œ ์—ฌ๋Ÿฌ ๋ฒˆ ์ž ๊ธ€ ์ˆ˜ ์žˆ์–ด.

std::lock_guard, std::unique_lock

RAII ํŒจํ„ด์œผ๋กœ ๋ฎคํ…์Šค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค. unique_lock์€ ๋” ์œ ์—ฐํ•œ ์ž ๊ธˆ ๊ด€๋ฆฌ๋ฅผ ์ œ๊ณตํ•ด.

std::condition_variable

์Šค๋ ˆ๋“œ ๊ฐ„ ์‹ ํ˜ธ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค.

std::atomic

์›์ž์  ์—ฐ์‚ฐ์„ ์ œ๊ณตํ•˜๋Š” ํ…œํ”Œ๋ฆฟ ํด๋ž˜์Šค. ๋ฝ ์—†์ด๋„ ์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•œ ์—ฐ์‚ฐ์ด ๊ฐ€๋Šฅํ•ด.

std::future, std::promise, std::async

๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋“ค.

C++14์˜ ๋™์‹œ์„ฑ ๊ฐœ์„  ์‚ฌํ•ญ ๐Ÿ”ง

C++14๋Š” C++11์˜ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๊ณ  ๊ฐœ์„ ํ–ˆ์–ด:

std::shared_lock

๊ณต์œ  ๋ฎคํ…์Šค(shared mutex)๋ฅผ ์œ„ํ•œ RAII ์Šคํƒ€์ผ์˜ ๋ฝ ํด๋ž˜์Šค. ์ฝ๊ธฐ-์“ฐ๊ธฐ ๋ฝ ํŒจํ„ด์— ์œ ์šฉํ•ด.

ํ–ฅ์ƒ๋œ std::atomic

atomic ํƒ€์ž…์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ๊ณผ ์ตœ์ ํ™”๊ฐ€ ์ด๋ฃจ์–ด์กŒ์–ด.

C++17์˜ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ ๐Ÿš€

C++17์€ ๋” ๋‹ค์–‘ํ•œ ๋™์‹œ์„ฑ ๋„๊ตฌ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์–ด:

std::shared_mutex

์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ฝ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ์“ฐ๊ธฐ๋Š” ๋ฐฐํƒ€์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜๋Š” ๋ฎคํ…์Šค. ์ฝ๊ธฐ ์ž‘์—…์ด ๋งŽ์€ ๊ฒฝ์šฐ์— ์„ฑ๋Šฅ ํ–ฅ์ƒ!

std::scoped_lock

์—ฌ๋Ÿฌ ๋ฎคํ…์Šค๋ฅผ ํ•œ ๋ฒˆ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ž ๊ทธ๋Š” RAII ์Šคํƒ€์ผ์˜ ๋ฝ ํด๋ž˜์Šค. ๊ต์ฐฉ ์ƒํƒœ ๋ฐฉ์ง€์— ์œ ์šฉํ•ด!

๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜

ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ๋ณ‘๋ ฌ ์‹คํ–‰ ์ •์ฑ…์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์–ด. std::execution::par๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋ผ!

๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์˜ˆ์‹œ:

#include <iostream>
#include <vector>
#include <algorithm>
#include <execution>  // C++17
#include <chrono>

int main() {
    std::vector<int> v(10000000);
    
    // ๋ฒกํ„ฐ ์ดˆ๊ธฐํ™”
    for (int i = 0; i < v.size(); ++i) {
        v[i] = i;
    }
    
    // ์ˆœ์ฐจ ์ •๋ ฌ ์‹œ๊ฐ„ ์ธก์ •
    auto start = std::chrono::high_resolution_clock::now();
    std::sort(v.begin(), v.end(), std::greater<>());
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> seq_time = end - start;
    
    // ๋ฒกํ„ฐ ๋‹ค์‹œ ์ดˆ๊ธฐํ™”
    for (int i = 0; i < v.size(); ++i) {
        v[i] = i;
    }
    
    // ๋ณ‘๋ ฌ ์ •๋ ฌ ์‹œ๊ฐ„ ์ธก์ •
    start = std::chrono::high_resolution_clock::now();
    std::sort(std::execution::par, v.begin(), v.end(), std::greater<>());
    end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> par_time = end - start;
    
    std::cout << "์ˆœ์ฐจ ์ •๋ ฌ ์‹œ๊ฐ„: " << seq_time.count() << "์ดˆ" << std::endl;
    std::cout << "๋ณ‘๋ ฌ ์ •๋ ฌ ์‹œ๊ฐ„: " << par_time.count() << "์ดˆ" << std::endl;
    std::cout << "์†๋„ ํ–ฅ์ƒ: " << seq_time.count() / par_time.count() << "๋ฐฐ" << std::endl;
    
    return 0;
}

์œ„ ์ฝ”๋“œ๋Š” ์ˆœ์ฐจ ์ •๋ ฌ๊ณผ ๋ณ‘๋ ฌ ์ •๋ ฌ์˜ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•˜๋Š” ์˜ˆ์‹œ์•ผ. ๋ฉ€ํ‹ฐ์ฝ”์–ด ์‹œ์Šคํ…œ์—์„œ๋Š” ๋ณ‘๋ ฌ ์ •๋ ฌ์ด ํ›จ์”ฌ ๋น ๋ฅผ ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

C++20์˜ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ ๐Ÿš€

C++20์€ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ ๋” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋“ค์„ ์ถ”๊ฐ€ํ–ˆ์–ด:

std::jthread

์†Œ๋ฉธ์ž์—์„œ ์ž๋™์œผ๋กœ join()์„ ํ˜ธ์ถœํ•˜๋Š” ๊ฐœ์„ ๋œ ์Šค๋ ˆ๋“œ ํด๋ž˜์Šค. ๋˜ํ•œ ์ทจ์†Œ ์š”์ฒญ์„ ์ง€์›ํ•ด!

std::stop_token, std::stop_source

ํ˜‘๋ ฅ์  ์ทจ์†Œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋“ค. ์Šค๋ ˆ๋“œ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ค‘๋‹จ์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด.

std::latch, std::barrier

์Šค๋ ˆ๋“œ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•œ ์นด์šดํŠธ๋‹ค์šด ๋ฉ”์ปค๋‹ˆ์ฆ˜. ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ํŠน์ • ์ง€์ ์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•  ์ˆ˜ ์žˆ์–ด.

std::counting_semaphore, std::binary_semaphore

์„ธ๋งˆํฌ์–ด ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋“ค. ๋ฆฌ์†Œ์Šค ์นด์šดํŒ…์— ์œ ์šฉํ•ด.

std::atomic_ref

๊ธฐ์กด ๊ฐ์ฒด์— ๋Œ€ํ•œ ์›์ž์  ์ฐธ์กฐ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค. ์›์ž์  ์—ฐ์‚ฐ์„ ๋” ์œ ์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.

C++20์˜ std::jthread ์˜ˆ์‹œ:

#include <iostream>
#include <thread>
#include <chrono>
#include <stop_token>  // C++20

void worker(std::stop_token stoken) {
    int counter = 0;
    
    // stop_token์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธํ•˜๋ฉฐ ์ž‘์—… ์ˆ˜ํ–‰
    while (!stoken.stop_requested() && counter < 10) {
        std::cout << "์ž‘์—… ์ค‘... " << counter++ << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    
    if (stoken.stop_requested()) {
        std::cout << "์ž‘์—…์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค." << std::endl;
    } else {
        std::cout << "์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค." << std::endl;
    }
}

int main() {
    std::jthread t(worker);  // jthread ์ƒ์„ฑ
    
    // 3์ดˆ ํ›„ ์Šค๋ ˆ๋“œ ์ทจ์†Œ ์š”์ฒญ
    std::this_thread::sleep_for(std::chrono::seconds(3));
    t.request_stop();
    
    // jthread๋Š” ์†Œ๋ฉธ์ž์—์„œ ์ž๋™์œผ๋กœ join() ํ˜ธ์ถœ
    std::cout << "๋ฉ”์ธ ํ•จ์ˆ˜ ์ข…๋ฃŒ" << std::endl;
    
    return 0;
}

std::jthread๋Š” ์ž๋™ join๊ณผ ํ˜‘๋ ฅ์  ์ทจ์†Œ๋ฅผ ์ง€์›ํ•ด์„œ ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ๊ฐ€ ํ›จ์”ฌ ์‰ฌ์›Œ์กŒ์–ด! ์ด์ œ ์Šค๋ ˆ๋“œ ๊ฐ์ฒด๊ฐ€ ์†Œ๋ฉธ๋  ๋•Œ ์ž๋™์œผ๋กœ join()์ด ํ˜ธ์ถœ๋˜๋ฏ€๋กœ ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ์—๋„ ์•ˆ์ „ํ•ด. ๐Ÿ‘

C++11 thread, mutex atomic, future C++14 shared_lock ํ–ฅ์ƒ๋œ atomic C++17 shared_mutex ๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ C++20 jthread, semaphore latch, barrier C++ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์˜ ๋ฐœ์ „ ๋ฒ„์ „์ด ์˜ฌ๋ผ๊ฐˆ์ˆ˜๋ก ๋” ๊ฐ•๋ ฅํ•˜๊ณ  ์•ˆ์ „ํ•œ ๋„๊ตฌ ์ œ๊ณต

6. ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ Future/Promise ๐Ÿ”ฎ

์ง€๊ธˆ๊นŒ์ง€๋Š” ์ฃผ๋กœ ์Šค๋ ˆ๋“œ๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค์–ด. ํ•˜์ง€๋งŒ C++์—์„œ๋Š” ๋” ๋†’์€ ์ˆ˜์ค€์˜ ์ถ”์ƒํ™”๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋„๊ตฌ๋“ค๋„ ์žˆ์–ด! ๐Ÿงฉ

std::async: ๊ฐ„ํŽธํ•œ ๋น„๋™๊ธฐ ์‹คํ–‰ ๐Ÿš€

std::async๋Š” ํ•จ์ˆ˜๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๊ณ  std::future๋ฅผ ํ†ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜. ์Šค๋ ˆ๋“œ ์ƒ์„ฑ๊ณผ ๊ด€๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ฃผ๋‹ˆ ํŽธ๋ฆฌํ•˜์ง€!

#include <iostream>
#include <future>
#include <chrono>
#include <string>

// ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ณ„์‚ฐ ํ•จ์ˆ˜
int compute_value(int x) {
    std::cout << "๊ณ„์‚ฐ ์‹œ์ž‘: " << x << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));  // ๋ฌด๊ฑฐ์šด ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    return x * x;
}

std::string fetch_data(const std::string& url) {
    std::cout << "๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘: " << url << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));  // ๋„คํŠธ์›Œํฌ ์ง€์—ฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    return "Data from " + url;
}

int main() {
    // ๋น„๋™๊ธฐ ์ž‘์—… ์‹œ์ž‘
    std::future<int> result1 = std::async(compute_value, 42);
    std::future<std::string> result2 = std::async(fetch_data, "example.com/api");
    
    std::cout << "๋น„๋™๊ธฐ ์ž‘์—…์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰ ์ค‘..." << std::endl;
    
    // ๋‹ค๋ฅธ ์ž‘์—… ์ˆ˜ํ–‰
    std::cout << "๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋‹ค๋ฅธ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    
    // ๊ฒฐ๊ณผ ๊ฐ€์ ธ์˜ค๊ธฐ (ํ•„์š”ํ•  ๋•Œ๊นŒ์ง€ ๋ธ”๋ก๋จ)
    std::cout << "์ฒซ ๋ฒˆ์งธ ๊ฒฐ๊ณผ: " << result1.get() << std::endl;
    std::cout << "๋‘ ๋ฒˆ์งธ ๊ฒฐ๊ณผ: " << result2.get() << std::endl;
    
    return 0;
}

std::async๋Š” ์‹คํ–‰ ์ •์ฑ…์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด:

std::launch::async

ํ•จ์ˆ˜๋ฅผ ์ƒˆ ์Šค๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•ด.

std::launch::deferred

ํ•จ์ˆ˜ ์‹คํ–‰์„ ์ง€์—ฐ์‹œํ‚ค๊ณ , future.get()์ด ํ˜ธ์ถœ๋  ๋•Œ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•ด.

std::launch::async | std::launch::deferred

๊ธฐ๋ณธ๊ฐ’์œผ๋กœ, ๊ตฌํ˜„์— ๋”ฐ๋ผ ๋น„๋™๊ธฐ ๋˜๋Š” ์ง€์—ฐ ์‹คํ–‰ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ด.

// ๋ช…์‹œ์ ์œผ๋กœ ๋น„๋™๊ธฐ ์‹คํ–‰ ์ง€์ •
std::future<int> result = std::async(std::launch::async, compute_value, 42);

// ์ง€์—ฐ ์‹คํ–‰ ์ง€์ • (get() ํ˜ธ์ถœ ์‹œ๊นŒ์ง€ ์‹คํ–‰๋˜์ง€ ์•Š์Œ)
std::future<int> deferred_result = std::async(std::launch::deferred, compute_value, 24);

std::future์™€ std::promise: ๋น„๋™๊ธฐ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ ๐Ÿ”„

std::future์™€ std::promise๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ์ฑ„๋„์„ ์ œ๊ณตํ•ด. promise๋Š” ๊ฒฐ๊ณผ๋ฅผ ์„ค์ •ํ•˜๊ณ , future๋Š” ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์–ด!

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

void producer(std::promise<int> promise) {
    std::cout << "์ƒ์‚ฐ์ž: ๊ฐ’ ๊ณ„์‚ฐ ์ค‘..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    int result = 42;  // ๊ณ„์‚ฐ๋œ ๊ฒฐ๊ณผ
    promise.set_value(result);  // promise์— ๊ฒฐ๊ณผ ์„ค์ •
    
    std::cout << "์ƒ์‚ฐ์ž: ๊ฐ’ ์„ค์ • ์™„๋ฃŒ!" << std::endl;
}

void consumer(std::future<int> future) {
    std::cout << "์†Œ๋น„์ž: ๊ฒฐ๊ณผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ค‘..." << std::endl;
    
    int value = future.get();  // ๊ฒฐ๊ณผ๊ฐ€ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ๋ธ”๋ก
    
    std::cout << "์†Œ๋น„์ž: ๊ฒฐ๊ณผ ๋ฐ›์Œ - " << value << std::endl;
}

int main() {
    std::promise<int> promise;
    std::future<int> future = promise.get_future();
    
    std::thread producer_thread(producer, std::move(promise));
    std::thread consumer_thread(consumer, std::move(future));
    
    producer_thread.join();
    consumer_thread.join();
    
    return 0;
}

์œ„ ์ฝ”๋“œ์—์„œ producer ํ•จ์ˆ˜๋Š” promise์— ๊ฐ’์„ ์„ค์ •ํ•˜๊ณ , consumer ํ•จ์ˆ˜๋Š” future์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์™€. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‘ ์Šค๋ ˆ๋“œ ๊ฐ„์— ๊ฐ’์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ”„

์ƒ์‚ฐ์ž ์Šค๋ ˆ๋“œ ์†Œ๋น„์ž ์Šค๋ ˆ๋“œ Promise Future ๋น„๋™๊ธฐ ์ฑ„๋„ 42

์˜ˆ์™ธ ์ฒ˜๋ฆฌ์™€ std::promise ๐Ÿšจ

std::promise๋Š” ๊ฐ’๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์˜ˆ์™ธ๋„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์–ด. ๋น„๋™๊ธฐ ์ž‘์—…์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ future๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋กœ ์ „ํŒŒํ•  ์ˆ˜ ์žˆ์ง€!

#include <iostream>
#include <future>
#include <thread>
#include <stdexcept>

void task_with_exceptions(std::promise<int> promise) {
    try {
        std::cout << "์ž‘์—… ์‹œ์ž‘..." << std::endl;
        
        // ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
        if (rand() % 2 == 0) {
            throw std::runtime_error("์ž‘์—… ์‹คํŒจ!");
        }
        
        // ์ •์ƒ ์™„๋ฃŒ
        promise.set_value(42);
    }
    catch (...) {
        // ํ˜„์žฌ ์˜ˆ์™ธ๋ฅผ promise๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ
        promise.set_exception(std::current_exception());
    }
}

int main() {
    srand(time(nullptr));
    
    std::promise<int> promise;
    std::future<int> future = promise.get_future();
    
    std::thread t(task_with_exceptions, std::move(promise));
    
    try {
        // future.get()์€ promise์—์„œ ์„ค์ •๋œ ์˜ˆ์™ธ๋ฅผ ๋‹ค์‹œ ๋˜์ง
        int result = future.get();
        std::cout << "์ž‘์—… ์„ฑ๊ณต! ๊ฒฐ๊ณผ: " << result << std::endl;
    }
    catch (const std::exception& e) {
        std::cout << "์ž‘์—… ์‹คํŒจ! ์˜ˆ์™ธ: " << e.what() << std::endl;
    }
    
    t.join();
    return 0;
}

์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋น„๋™๊ธฐ ์ž‘์—…์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด. ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ํ›จ์”ฌ ๊น”๋”ํ•ด์ง€์ง€! ๐Ÿ‘

std::shared_future: ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๊ฒฐ๊ณผ ๊ณต์œ  ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ

std::future๋Š” ํ•œ ๋ฒˆ๋งŒ get()์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์–ด. ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด std::shared_future๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ด:

#include <iostream>
#include <future>
#include <thread>
#include <vector>

void reader(std::shared_future<int> future, int id) {
    std::cout << "๋ฆฌ๋” " << id << ": ๊ฒฐ๊ณผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ค‘..." << std::endl;
    
    int value = future.get();  // ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ํ˜ธ์ถœ ๊ฐ€๋Šฅ
    
    std::cout << "๋ฆฌ๋” " << id << ": ๊ฒฐ๊ณผ ๋ฐ›์Œ - " << value << std::endl;
}

int main() {
    std::promise<int> promise;
    std::shared_future<int> shared_future = promise.get_future().share();
    
    std::vector<std::thread> readers;
    
    // ์—ฌ๋Ÿฌ ๋ฆฌ๋” ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    for (int i = 0; i < 5; ++i) {
        readers.emplace_back(reader, shared_future, i);
    }
    
    std::cout << "๋ฉ”์ธ: 3์ดˆ ํ›„ ๊ฒฐ๊ณผ ์„ค์ •..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));
    
    // ๋ชจ๋“  ๋ฆฌ๋”๊ฐ€ ๋™์‹œ์— ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›๊ฒŒ ๋จ
    promise.set_value(100);
    
    for (auto& t : readers) {
        t.join();
    }
    
    return 0;
}

std::shared_future๋Š” ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด. ์ด๋Š” ํ•œ ๋ฒˆ ๊ณ„์‚ฐ๋œ ๊ฒฐ๊ณผ๋ฅผ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•ด! ๐ŸŒŸ

7. ์Šค๋ ˆ๋“œ ํ’€๊ณผ ์ž‘์—… ํ ๊ตฌํ˜„ํ•˜๊ธฐ ๐Ÿงต

์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋งค๋ฒˆ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์†Œ๋ฉธ์‹œํ‚ค๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ด์•ผ. ์Šค๋ ˆ๋“œ ํ’€(Thread Pool)์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋œ ์Šค๋ ˆ๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•ด์„œ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

์Šค๋ ˆ๋“œ ํ’€์˜ ์žฅ์  ๐ŸŒŸ

์Šค๋ ˆ๋“œ ์ƒ์„ฑ ์˜ค๋ฒ„ํ—ค๋“œ ๊ฐ์†Œ

์Šค๋ ˆ๋“œ ์ƒ์„ฑ์€ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ์ž‘์—…์ด์•ผ. ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด๋‘๋ฉด ์ด ๋น„์šฉ์„ ์ค„์ผ ์ˆ˜ ์žˆ์–ด.

๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰ ์ œํ•œ

๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ์Šค๋ ˆ๋“œ ์ˆ˜๋ฅผ ์ œํ•œํ•ด์„œ ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.

์ž‘์—… ๊ด€๋ฆฌ ์šฉ์ด

์ž‘์—… ํ๋ฅผ ํ†ตํ•ด ์ž‘์—…์„ ์Šค์ผ€์ค„๋งํ•˜๊ณ  ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์–ด.

๊ฐ„๋‹จํ•œ ์Šค๋ ˆ๋“œ ํ’€ ๊ตฌํ˜„ํ•˜๊ธฐ ๐Ÿ› ๏ธ

C++์—์„œ ๊ฐ„๋‹จํ•œ ์Šค๋ ˆ๋“œ ํ’€์„ ๊ตฌํ˜„ํ•ด๋ณด์ž:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#include <vector>
#include <future>

class ThreadPool {
public:
    // ์Šค๋ ˆ๋“œ ํ’€ ์ƒ์„ฑ์ž: ์ง€์ •๋œ ์ˆ˜์˜ ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    ThreadPool(size_t num_threads) : stop(false) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    
                    {
                        std::unique_lock<std::mutex> lock(queue_mutex);
                        
                        // ์ž‘์—…์ด ์žˆ๊ฑฐ๋‚˜ ์ค‘์ง€ ์‹ ํ˜ธ๊ฐ€ ์˜ฌ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
                        condition.wait(lock, [this] {
                            return stop || !tasks.empty();
                        });
                        
                        // ์ค‘์ง€ ์‹ ํ˜ธ๊ฐ€ ์™”๊ณ  ์ž‘์—…์ด ์—†์œผ๋ฉด ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ
                        if (stop && tasks.empty()) {
                            return;
                        }
                        
                        // ์ž‘์—… ํ์—์„œ ์ž‘์—… ๊ฐ€์ ธ์˜ค๊ธฐ
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    
                    // ์ž‘์—… ์‹คํ–‰
                    task();
                }
            });
        }
    }
    
    // ์†Œ๋ฉธ์ž: ๋ชจ๋“  ์Šค๋ ˆ๋“œ ์ •๋ฆฌ
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        
        // ๋ชจ๋“  ์Šค๋ ˆ๋“œ๋ฅผ ๊นจ์›Œ์„œ ์ข…๋ฃŒํ•˜๋„๋ก ํ•จ
        condition.notify_all();
        
        // ๋ชจ๋“  ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ์กฐ์ธ
        for (std::thread &worker : workers) {
            worker.join();
        }
    }
    
    // ์ž‘์—… ์ถ”๊ฐ€ ๋ฐ future ๋ฐ˜ํ™˜
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
        using return_type = typename std::result_of<F(Args...)>::type;
        
        // ์ž‘์—…์„ ํŒจํ‚ค์ง€ ํƒœ์Šคํฌ๋กœ ๋ž˜ํ•‘
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
        // ์ž‘์—…์˜ future ๊ฐ€์ ธ์˜ค๊ธฐ
        std::future<return_type> result = task->get_future();
        
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            
            // ์ค‘์ง€ ์ƒํƒœ์—์„œ๋Š” ์ƒˆ ์ž‘์—… ๊ฑฐ๋ถ€
            if (stop) {
                throw std::runtime_error("์Šค๋ ˆ๋“œ ํ’€์ด ์ค‘์ง€๋œ ์ƒํƒœ์—์„œ ์ž‘์—… ์ถ”๊ฐ€ ์‹œ๋„");
            }
            
            // ์ž‘์—… ํ์— ์ž‘์—… ์ถ”๊ฐ€
            tasks.emplace([task]() { (*task)(); });
        }
        
        // ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ํ•˜๋‚˜ ๊นจ์šฐ๊ธฐ
        condition.notify_one();
        
        return result;
    }
    
private:
    // ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ๋ฒกํ„ฐ
    std::vector<std::thread> workers;
    
    // ์ž‘์—… ํ
    std::queue<std::function<void()>> tasks;
    
    // ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•œ ๋ฎคํ…์Šค์™€ ์กฐ๊ฑด ๋ณ€์ˆ˜
    std::mutex queue_mutex;
    std::condition_variable condition;
    
    // ์ค‘์ง€ ํ”Œ๋ž˜๊ทธ
    bool stop;
};

// ์‚ฌ์šฉ ์˜ˆ์‹œ
int main() {
    // ํ•˜๋“œ์›จ์–ด ๋™์‹œ์„ฑ ์ˆ˜์ค€์— ๋งž์ถฐ ์Šค๋ ˆ๋“œ ํ’€ ์ƒ์„ฑ
    ThreadPool pool(std::thread::hardware_concurrency());
    
    // ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  future ๋ฒกํ„ฐ
    std::vector<std::future<int>> results;
    
    // ์ž‘์—… ์ถ”๊ฐ€
    for (int i = 0; i < 10; ++i) {
        // ๋žŒ๋‹ค ํ•จ์ˆ˜๋ฅผ ์ž‘์—…์œผ๋กœ ์ถ”๊ฐ€
        auto result = pool.enqueue([i] {
            std::cout << "์ž‘์—… " << i << " ์‹คํ–‰ ์ค‘ (์Šค๋ ˆ๋“œ ID: " 
                      << std::this_thread::get_id() << ")" << std::endl;
            
            // ๋ฌด๊ฑฐ์šด ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
            std::this_thread::sleep_for(std::chrono::seconds(1));
            
            return i * i;
        });
        
        results.push_back(std::move(result));
    }
    
    // ๋ชจ๋“  ๊ฒฐ๊ณผ ์ถœ๋ ฅ
    for (auto &result : results) {
        std::cout << "๊ฒฐ๊ณผ: " << result.get() << std::endl;
    }
    
    return 0;
}

์œ„ ์ฝ”๋“œ๋Š” ์ž‘์—… ํ์™€ ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ํ’€์„ ๊ตฌํ˜„ํ•œ ์˜ˆ์‹œ์•ผ. ์ž‘์—…์ž ์Šค๋ ˆ๋“œ๋“ค์€ ํ์—์„œ ์ž‘์—…์„ ๊ฐ€์ ธ์™€ ์‹คํ–‰ํ•˜๊ณ , ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” enqueue ๋ฉ”์„œ๋“œ๋กœ ์ž‘์—…์„ ์ถ”๊ฐ€ํ•ด. ๊ฐ ์ž‘์—…์€ std::future๋ฅผ ๋ฐ˜ํ™˜ํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด! ๐Ÿ”„

์ž‘์—… ํ ์ž‘์—… 1 ์ž‘์—… 2 ์ž‘์—… 3 ์ž‘์—… 4 ์Šค๋ ˆ๋“œ ํ’€ W1 W2 W3 W4 W5 W6 ์ž‘์—… ํ• ๋‹น ๊ฒฐ๊ณผ (Future ๊ฐ์ฒด) ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ ์ž‘์—… ์ œ์ถœ ๊ฒฐ๊ณผ ์ˆ˜์‹ 

C++17 ๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ™œ์šฉํ•˜๊ธฐ ๐Ÿ”„

C++17๋ถ€ํ„ฐ๋Š” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋ณ‘๋ ฌ ์‹คํ–‰ ์ •์ฑ…์„ ์ง€์›ํ•ด. ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ง์ ‘ ์Šค๋ ˆ๋“œ ํ’€์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ ๋„ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด!

#include <iostream>
#include <vector>
#include <algorithm>
#include <execution>  // C++17
#include <chrono>
#include <random>

int main() {
    // ํฐ ๋ฒกํ„ฐ ์ƒ์„ฑ
    std::vector<int> data(10000000);
    
    // ๋‚œ์ˆ˜๋กœ ๋ฒกํ„ฐ ์ฑ„์šฐ๊ธฐ
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> dist(1, 1000000);
    
    for (auto& item : data) {
        item = dist(gen);
    }
    
    // ์ˆœ์ฐจ ์ •๋ ฌ
    auto seq_data = data;
    auto start = std::chrono::high_resolution_clock::now();
    std::sort(seq_data.begin(), seq_data.end());
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> seq_time = end - start;
    
    // ๋ณ‘๋ ฌ ์ •๋ ฌ
    auto par_data = data;
    start = std::chrono::high_resolution_clock::now();
    std::sort(std::execution::par, par_data.begin(), par_data.end());
    end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> par_time = end - start;
    
    // ๋ณ‘๋ ฌ ๋ฒกํ„ฐํ™” ์ •๋ ฌ
    auto par_unseq_data = data;
    start = std::chrono::high_resolution_clock::now();
    std::sort(std::execution::par_unseq, par_unseq_data.begin(), par_unseq_data.end());
    end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> par_unseq_time = end - start;
    
    std::cout << "์ˆœ์ฐจ ์ •๋ ฌ ์‹œ๊ฐ„: " << seq_time.count() << "์ดˆ" << std::endl;
    std::cout << "๋ณ‘๋ ฌ ์ •๋ ฌ ์‹œ๊ฐ„: " << par_time.count() << "์ดˆ" << std::endl;
    std::cout << "๋ณ‘๋ ฌ ๋ฒกํ„ฐํ™” ์ •๋ ฌ ์‹œ๊ฐ„: " << par_unseq_time.count() << "์ดˆ" << std::endl;
    
    return 0;
}

C++17์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‹คํ–‰ ์ •์ฑ…์„ ์ œ๊ณตํ•ด:

std::execution::seq

์ˆœ์ฐจ ์‹คํ–‰ (๊ธฐ๋ณธ๊ฐ’)

std::execution::par

๋ณ‘๋ ฌ ์‹คํ–‰ (์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ ์‚ฌ์šฉ)

std::execution::par_unseq

๋ณ‘๋ ฌ ๋ฐ ๋ฒกํ„ฐํ™” ์‹คํ–‰ (SIMD ๋ช…๋ น์–ด ํ™œ์šฉ ๊ฐ€๋Šฅ)

์ด ์‹คํ–‰ ์ •์ฑ…์€ std::sort, std::for_each, std::transform ๋“ฑ ๋‹ค์–‘ํ•œ ํ‘œ์ค€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ๋‚ด๋ถ€์ ์œผ๋กœ ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†์ง€! ๐Ÿ‘

8. ์‹ค์ „ ์˜ˆ์ œ: ๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„ ๐Ÿงฎ

์ด์ œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ๊ณผ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ™œ์šฉํ•œ ์‹ค์ „ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด์ž! ๋ช‡ ๊ฐ€์ง€ ์ผ๋ฐ˜์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ณ‘๋ ฌํ™”ํ•ด์„œ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๐Ÿš€

๋ณ‘๋ ฌ ๋งต-๋ฆฌ๋“€์Šค(Map-Reduce) ๊ตฌํ˜„ ๐Ÿ—บ๏ธ

๋งต-๋ฆฌ๋“€์Šค๋Š” ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์— ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์ด์•ผ. ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„ํ• ํ•˜๊ณ (๋งต), ๊ฐ ๋ถ€๋ถ„์„ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•œ ๋‹ค์Œ, ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋Š”(๋ฆฌ๋“€์Šค) ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ด.

#include <iostream>
#include <vector>
#include <thread>
#include <numeric>
#include <algorithm>
#include <functional>
#include <chrono>

// ๋ณ‘๋ ฌ ๋งต-๋ฆฌ๋“€์Šค ํ•จ์ˆ˜
template<typename Iterator, typename T, typename MapFunc, typename ReduceFunc>
T parallel_map_reduce(Iterator first, Iterator last, T init, 
                     MapFunc map_func, ReduceFunc reduce_func, 
                     size_t num_threads = std::thread::hardware_concurrency()) {
    
    // ๋ฐ์ดํ„ฐ ํฌ๊ธฐ
    const auto length = std::distance(first, last);
    
    // ์Šค๋ ˆ๋“œ ์ˆ˜ ์กฐ์ • (๋ฐ์ดํ„ฐ๊ฐ€ ์ ์œผ๋ฉด ์Šค๋ ˆ๋“œ ์ˆ˜ ์ค„์ด๊ธฐ)
    num_threads = std::min(num_threads, static_cast<size_t>(length));
    
    if (num_threads <= 1) {
        // ๋‹จ์ผ ์Šค๋ ˆ๋“œ ์ฒ˜๋ฆฌ
        return std::transform_reduce(first, last, init, reduce_func, map_func);
    }
    
    // ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฒ˜๋ฆฌํ•  ๋ฐ์ดํ„ฐ ๋ฒ”์œ„ ๊ณ„์‚ฐ
    const size_t chunk_size = length / num_threads;
    
    // ๊ฐ ์Šค๋ ˆ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  ๋ฒกํ„ฐ
    std::vector<T> results(num_threads, init);
    std::vector<std::thread> threads;
    
    // ๊ฐ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ ๋ฐ ์ž‘์—… ํ• ๋‹น
    for (size_t i = 0; i < num_threads; ++i) {
        auto chunk_begin = first + i * chunk_size;
        auto chunk_end = (i == num_threads - 1) ? last : chunk_begin + chunk_size;
        
        threads.emplace_back([=, &results]() {
            // ๋งต ๋‹จ๊ณ„: ๊ฐ ์š”์†Œ์— ๋งต ํ•จ์ˆ˜ ์ ์šฉ
            std::vector<T> mapped;
            mapped.reserve(std::distance(chunk_begin, chunk_end));
            
            std::transform(chunk_begin, chunk_end, std::back_inserter(mapped), map_func);
            
            // ๋ฆฌ๋“€์Šค ๋‹จ๊ณ„: ๋งต ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ๋“€์Šค ํ•จ์ˆ˜๋กœ ์ถ•์†Œ
            results[i] = std::reduce(mapped.begin(), mapped.end(), init, reduce_func);
        });
    }
    
    // ๋ชจ๋“  ์Šค๋ ˆ๋“œ ์™„๋ฃŒ ๋Œ€๊ธฐ
    for (auto& t : threads) {
        t.join();
    }
    
    // ์ตœ์ข… ๋ฆฌ๋“€์Šค: ๊ฐ ์Šค๋ ˆ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์นจ
    return std::reduce(results.begin(), results.end(), init, reduce_func);
}

int main() {
    // ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    std::vector<int> data(50000000);
    std::iota(data.begin(), data.end(), 1);  // 1๋ถ€ํ„ฐ 5์ฒœ๋งŒ๊นŒ์ง€ ์ฑ„์šฐ๊ธฐ
    
    // ๋งต ํ•จ์ˆ˜: ์ œ๊ณฑ
    auto map_func = [](int x) { return x * x; };
    
    // ๋ฆฌ๋“€์Šค ํ•จ์ˆ˜: ํ•ฉ๊ณ„
    auto reduce_func = std::plus<long long>();
    
    // ์ˆœ์ฐจ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ธก์ •
    auto start = std::chrono::high_resolution_clock::now();
    
    long long seq_result = std::transform_reduce(
        data.begin(), data.end(), 0LL, reduce_func, map_func);
    
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> seq_time = end - start;
    
    // ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ธก์ •
    start = std::chrono::high_resolution_clock::now();
    
    long long par_result = parallel_map_reduce(
        data.begin(), data.end(), 0LL, map_func, reduce_func);
    
    end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> par_time = end - start;
    
    // ๊ฒฐ๊ณผ ์ถœ๋ ฅ
    std::cout << "์ˆœ์ฐจ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ: " << seq_result << std::endl;
    std::cout << "๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ: " << par_result << std::endl;
    std::cout << "์ˆœ์ฐจ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„: " << seq_time.count() << "์ดˆ" << std::endl;
    std::cout << "๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„: " << par_time.count() << "์ดˆ" << std::endl;
    std::cout << "์†๋„ ํ–ฅ์ƒ: " << seq_time.count() / par_time.count() << "๋ฐฐ" << std::endl;
    
    return 0;
}

์œ„ ์ฝ”๋“œ๋Š” ๋ณ‘๋ ฌ ๋งต-๋ฆฌ๋“€์Šค ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ตฌํ˜„ํ•œ ์˜ˆ์‹œ์•ผ. ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ์ฒญํฌ๋กœ ๋‚˜๋ˆ„๊ณ , ๊ฐ ์ฒญํฌ๋ฅผ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•œ ๋‹ค์Œ, ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์ณ. ์ด ๋ฐฉ์‹์€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณ‘๋ ฌํ™”ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ํŒจํ„ด์ด์•ผ! ๐Ÿ”„

์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๋ฐ์ดํ„ฐ ๋ถ„ํ•  ์ฒญํฌ 1 ์ฒญํฌ 2 ์ฒญํฌ 3 ์ฒญํฌ 4 ๋งต ๋‹จ๊ณ„ (๋ณ‘๋ ฌ) ๋งต ๊ฒฐ๊ณผ 1 ๋งต ๊ฒฐ๊ณผ 2 ๋งต ๊ฒฐ๊ณผ 3 ๋งต ๊ฒฐ๊ณผ 4 ๋กœ์ปฌ ๋ฆฌ๋“€์Šค (๋ณ‘๋ ฌ) ๋ถ€๋ถ„ ๊ฒฐ๊ณผ 1 ๋ถ€๋ถ„ ๊ฒฐ๊ณผ 2 ๋ถ€๋ถ„ ๊ฒฐ๊ณผ 3 ๋ถ€๋ถ„ ๊ฒฐ๊ณผ 4 ์ตœ์ข… ๋ฆฌ๋“€์Šค ์ตœ์ข… ๊ฒฐ๊ณผ

๋ณ‘๋ ฌ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๐Ÿ–ผ๏ธ

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋Š” ํ”ฝ์…€ ๋‹จ์œ„ ์—ฐ์‚ฐ์ด ๋งŽ์•„ ๋ณ‘๋ ฌํ™”์— ๋งค์šฐ ์ ํ•ฉํ•ด. ๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ๋ธ”๋Ÿฌ ํ•„ํ„ฐ๋ฅผ ๋ณ‘๋ ฌ๋กœ ๊ตฌํ˜„ํ•ด๋ณด์ž:

#include <iostream>
#include <vector>
#include <thread>
#include <algorithm>
#include <chrono>
#include <cmath>

// ๊ฐ„๋‹จํ•œ RGB ํ”ฝ์…€ ๊ตฌ์กฐ์ฒด
struct Pixel {
    unsigned char r, g, b;
    
    Pixel() : r(0), g(0), b(0) {}
    Pixel(unsigned char r, unsigned char g, unsigned char b) : r(r), g(g), b(b) {}
};

// ๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ํด๋ž˜์Šค
class Image {
public:
    Image(int width, int height) : width_(width), height_(height), pixels_(width * height) {}
    
    Pixel& at(int x, int y) {
        return pixels_[y * width_ + x];
    }
    
    const Pixel& at(int x, int y) const {
        return pixels_[y * width_ + x];
    }
    
    int width() const { return width_; }
    int height() const { return height_; }
    
private:
    int width_, height_;
    std::vector<Pixel> pixels_;
};

// ์ˆœ์ฐจ์  ๋ธ”๋Ÿฌ ํ•„ํ„ฐ ์ ์šฉ
void apply_blur_sequential(const Image& input, Image& output, int blur_radius) {
    for (int y = 0; y < input.height(); ++y) {
        for (int x = 0; x < input.width(); ++x) {
            int r_sum = 0, g_sum = 0, b_sum = 0;
            int count = 0;
            
            // ์ฃผ๋ณ€ ํ”ฝ์…€ ํ‰๊ท  ๊ณ„์‚ฐ
            for (int dy = -blur_radius; dy <= blur_radius; ++dy) {
                for (int dx = -blur_radius; dx <= blur_radius; ++dx) {
                    int nx = x + dx;
                    int ny = y + dy;
                    
                    // ์ด๋ฏธ์ง€ ๊ฒฝ๊ณ„ ์ฒดํฌ
                    if (nx >= 0 && nx < input.width() && ny >= 0 && ny < input.height()) {
                        const Pixel& p = input.at(nx, ny);
                        r_sum += p.r;
                        g_sum += p.g;
                        b_sum += p.b;
                        count++;
                    }
                }
            }
            
            // ํ‰๊ท ๊ฐ’ ๊ณ„์‚ฐ
            output.at(x, y) = Pixel(r_sum / count, g_sum / count, b_sum / count);
        }
    }
}

// ๋ณ‘๋ ฌ ๋ธ”๋Ÿฌ ํ•„ํ„ฐ ์ ์šฉ
void apply_blur_parallel(const Image& input, Image& output, int blur_radius, int num_threads = std::thread::hardware_concurrency()) {
    std::vector<std::thread> threads;
    
    // ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฒ˜๋ฆฌํ•  ํ–‰ ๋ฒ”์œ„ ๊ณ„์‚ฐ
    int rows_per_thread = input.height() / num_threads;
    
    for (int t = 0; t < num_threads; ++t) {
        int start_row = t * rows_per_thread;
        int end_row = (t == num_threads - 1) ? input.height() : start_row + rows_per_thread;
        
        threads.emplace_back([&input, &output, blur_radius, start_row, end_row]() {
            for (int y = start_row; y < end_row; ++y) {
                for (int x = 0; x < input.width(); ++x) {
                    int r_sum = 0, g_sum = 0, b_sum = 0;
                    int count = 0;
                    
                    // ์ฃผ๋ณ€ ํ”ฝ์…€ ํ‰๊ท  ๊ณ„์‚ฐ
                    for (int dy = -blur_radius; dy <= blur_radius; ++dy) {
                        for (int dx = -blur_radius; dx <= blur_radius; ++dx) {
                            int nx = x + dx;
                            int ny = y + dy;
                            
                            // ์ด๋ฏธ์ง€ ๊ฒฝ๊ณ„ ์ฒดํฌ
                            if (nx >= 0 && nx < input.width() && ny >= 0 && ny < input.height()) {
                                const Pixel& p = input.at(nx, ny);
                                r_sum += p.r;
                                g_sum += p.g;
                                b_sum += p.b;
                                count++;
                            }
                        }
                    }
                    
                    // ํ‰๊ท ๊ฐ’ ๊ณ„์‚ฐ
                    output.at(x, y) = Pixel(r_sum / count, g_sum / count, b_sum / count);
                }
            }
        });
    }
    
    // ๋ชจ๋“  ์Šค๋ ˆ๋“œ ์™„๋ฃŒ ๋Œ€๊ธฐ
    for (auto& t : threads) {
        t.join();
    }
}

int main() {
    // ํ…Œ์ŠคํŠธ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (4K ํ•ด์ƒ๋„)
    const int width = 3840;
    const int height = 2160;
    
    Image input(width, height);
    Image output_seq(width, height);
    Image output_par(width, height);
    
    // ํ…Œ์ŠคํŠธ ์ด๋ฏธ์ง€ ์ดˆ๊ธฐํ™” (๊ทธ๋ผ๋ฐ์ด์…˜ ํŒจํ„ด)
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            input.at(x, y) = Pixel(
                static_cast<unsigned char>(255 * x / width),
                static_cast<unsigned char>(255 * y / height),
                static_cast<unsigned char>(255 * (x + y) / (width + height))
            );
        }
    }
    
    const int blur_radius = 5;
    
    // ์ˆœ์ฐจ ๋ธ”๋Ÿฌ ์‹œ๊ฐ„ ์ธก์ •
    auto start = std::chrono::high_resolution_clock::now();
    apply_blur_sequential(input, output_seq, blur_radius);
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> seq_time = end - start;
    
    // ๋ณ‘๋ ฌ ๋ธ”๋Ÿฌ ์‹œ๊ฐ„ ์ธก์ •
    start = std::chrono::high_resolution_clock::now();
    apply_blur_parallel(input, output_par, blur_radius);
    end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> par_time = end - start;
    
    // ๊ฒฐ๊ณผ ๊ฒ€์ฆ (๋‘ ์ถœ๋ ฅ์ด ๋™์ผํ•œ์ง€ ํ™•์ธ)
    bool results_match = true;
    for (int y = 0; y < height && results_match; ++y) {
        for (int x = 0; x < width && results_match; ++x) {
            const Pixel& p1 = output_seq.at(x, y);
            const Pixel& p2 = output_par.at(x, y);
            
            if (p1.r != p2.r || p1.g != p2.g || p1.b != p2.b) {
                results_match = false;
                std::cout << "๊ฒฐ๊ณผ ๋ถˆ์ผ์น˜! ์œ„์น˜: (" << x << ", " << y << ")" << std::endl;
            }
        }
    }
    
    std::cout << "์ˆœ์ฐจ ๋ธ”๋Ÿฌ ์‹œ๊ฐ„: " << seq_time.count() << "์ดˆ" << std::endl;
    std::cout << "๋ณ‘๋ ฌ ๋ธ”๋Ÿฌ ์‹œ๊ฐ„: " << par_time.count() << "์ดˆ" << std::endl;
    std::cout << "์†๋„ ํ–ฅ์ƒ: " << seq_time.count() / par_time.count() << "๋ฐฐ" << std::endl;
    std::cout << "๊ฒฐ๊ณผ ์ผ์น˜: " << (results_match ? "์˜ˆ" : "์•„๋‹ˆ์˜ค") << std::endl;
    
    return 0;
}

์œ„ ์ฝ”๋“œ๋Š” ์ด๋ฏธ์ง€ ๋ธ”๋Ÿฌ ํ•„ํ„ฐ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๊ทธ๋ฆฌ๊ณ  ๋ณ‘๋ ฌ๋กœ ๊ตฌํ˜„ํ•œ ์˜ˆ์‹œ์•ผ. ์ด๋ฏธ์ง€๋ฅผ ์ˆ˜ํ‰ ์ŠคํŠธ๋ฆฝ์œผ๋กœ ๋‚˜๋ˆ„๊ณ  ๊ฐ ์ŠคํŠธ๋ฆฝ์„ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•ด. ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ, ๊ณผํ•™ ๊ณ„์‚ฐ, ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋“ฑ์˜ ๋ถ„์•ผ์—์„œ ์ด๋Ÿฐ ๋ณ‘๋ ฌํ™” ๊ธฐ๋ฒ•์ด ๋งŽ์ด ์‚ฌ์šฉ๋ผ! ๐Ÿ–ผ๏ธ

๋ณ‘๋ ฌ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๐Ÿ”„

๋งˆ์ง€๋ง‰์œผ๋กœ, ๋ณ‘๋ ฌ ๋ณ‘ํ•ฉ ์ •๋ ฌ(Merge Sort) ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ตฌํ˜„ํ•ด๋ณด์ž:

#include <iostream>
#include <vector>
#include <thread>
#include <algorithm>
#include <chrono>
#include <future>

// ๋ณ‘ํ•ฉ ํ•จ์ˆ˜: ๋‘ ์ •๋ ฌ๋œ ๋ถ€๋ถ„ ๋ฐฐ์—ด์„ ํ•˜๋‚˜๋กœ ๋ณ‘ํ•ฉ
template<typename T>
void merge(std::vector<T>& arr, std::vector<T>& temp, int left, int mid, int right) {
    int i = left;      // ์™ผ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด ์ธ๋ฑ์Šค
    int j = mid + 1;   // ์˜ค๋ฅธ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด ์ธ๋ฑ์Šค
    int k = left;      // ์ž„์‹œ ๋ฐฐ์—ด ์ธ๋ฑ์Šค
    
    // ๋‘ ๋ถ€๋ถ„ ๋ฐฐ์—ด์„ ๋น„๊ตํ•˜๋ฉฐ ์ž„์‹œ ๋ฐฐ์—ด์— ๋ณ‘ํ•ฉ
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    
    // ๋‚จ์€ ์š”์†Œ ๋ณต์‚ฌ
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    
    while (j <= right) {
        temp[k++] = arr[j++];
    }
    
    // ์ž„์‹œ ๋ฐฐ์—ด์—์„œ ์›๋ณธ ๋ฐฐ์—ด๋กœ ๋ณต์‚ฌ
    for (i = left; i <= right; i++) {
        arr[i] = temp[i];
    }
}

// ์ˆœ์ฐจ์  ๋ณ‘ํ•ฉ ์ •๋ ฌ
template<typename T>
void merge_sort_sequential(std::vector<T>& arr, std::vector<T>& temp, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        
        // ๋ถ„ํ• 
        merge_sort_sequential(arr, temp, left, mid);
        merge_sort_sequential(arr, temp, mid + 1, right);
        
        // ๋ณ‘ํ•ฉ
        merge(arr, temp, left, mid, right);
    }
}

// ๋ณ‘๋ ฌ ๋ณ‘ํ•ฉ ์ •๋ ฌ
template<typename T>
void merge_sort_parallel(std::vector<T>& arr, std::vector<T>& temp, int left, int right, int depth = 0) {
    // ์ตœ๋Œ€ ์žฌ๊ท€ ๊นŠ์ด ๋˜๋Š” ์ž‘์€ ๋ฐฐ์—ด์— ๋Œ€ํ•ด์„œ๋Š” ์ˆœ์ฐจ ์ •๋ ฌ ์‚ฌ์šฉ
    if (depth >= 3 || right - left < 1000) {
        merge_sort_sequential(arr, temp, left, right);
        return;
    }
    
    int mid = left + (right - left) / 2;
    
    // ์™ผ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด์„ ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ์ •๋ ฌ
    auto future = std::async(std::launch::async, [&arr, &temp, left, mid, depth]() {
        merge_sort_parallel(arr, temp, left, mid, depth + 1);
    });
    
    // ํ˜„์žฌ ์Šค๋ ˆ๋“œ์—์„œ ์˜ค๋ฅธ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด ์ •๋ ฌ
    merge_sort_parallel(arr, temp, mid + 1, right, depth + 1);
    
    // ์™ผ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด ์ •๋ ฌ ์™„๋ฃŒ ๋Œ€๊ธฐ
    future.wait();
    
    // ๋‘ ๋ถ€๋ถ„ ๋ฐฐ์—ด ๋ณ‘ํ•ฉ
    merge(arr, temp, left, mid, right);
}

int main() {
    const int size = 10000000;  // 1์ฒœ๋งŒ ๊ฐœ ์š”์†Œ
    
    // ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    std::vector<int> data(size);
    std::vector<int> data_copy(size);
    std::vector<int> temp(size);
    std::vector<int> temp_copy(size);
    
    // ๋‚œ์ˆ˜๋กœ ๋ฒกํ„ฐ ์ฑ„์šฐ๊ธฐ
    std::srand(std::time(nullptr));
    for (int i = 0; i < size; ++i) {
        data[i] = std::rand() % 1000000;
    }
    data_copy = data;  // ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ
    
    // ์ˆœ์ฐจ ์ •๋ ฌ ์‹œ๊ฐ„ ์ธก์ •
    auto start = std::chrono::high_resolution_clock::now();
    merge_sort_sequential(data, temp, 0, size - 1);
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> seq_time = end - start;
    
    // ๋ณ‘๋ ฌ ์ •๋ ฌ ์‹œ๊ฐ„ ์ธก์ •
    start = std::chrono::high_resolution_clock::now();
    merge_sort_parallel(data_copy, temp_copy, 0, size - 1);
    end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> par_time = end - start;
    
    // ๊ฒฐ๊ณผ ๊ฒ€์ฆ
    bool is_sorted_seq = std::is_sorted(data.begin(), data.end());
    bool is_sorted_par = std::is_sorted(data_copy.begin(), data_copy.end());
    bool results_match = data == data_copy;
    
    std::cout << "์ˆœ์ฐจ ์ •๋ ฌ ์‹œ๊ฐ„: " << seq_time.count() << "์ดˆ (์ •๋ ฌ๋จ: " << is_sorted_seq << ")" << std::endl;
    std::cout << "๋ณ‘๋ ฌ ์ •๋ ฌ ์‹œ๊ฐ„: " << par_time.count() << "์ดˆ (์ •๋ ฌ๋จ: " << is_sorted_par << ")" << std::endl;
    std::cout << "์†๋„ ํ–ฅ์ƒ: " << seq_time.count() / par_time.count() << "๋ฐฐ" << std::endl;
    std::cout << "๊ฒฐ๊ณผ ์ผ์น˜: " << results_match << std::endl;
    
    return 0;
}

๋ณ‘๋ ฌ ๋ณ‘ํ•ฉ ์ •๋ ฌ์€ ๋ถ„ํ•  ์ •๋ณต(divide and conquer) ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์ „ํ˜•์ ์ธ ์˜ˆ์‹œ์•ผ. ๋ฌธ์ œ๋ฅผ ์ž‘์€ ๋ถ€๋ถ„ ๋ฌธ์ œ๋กœ ๋‚˜๋ˆ„๊ณ , ๊ฐ ๋ถ€๋ถ„ ๋ฌธ์ œ๋ฅผ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ํ•ด๊ฒฐํ•œ ๋‹ค์Œ, ๊ฒฐ๊ณผ๋ฅผ ๋ณ‘ํ•ฉํ•ด. ์ด๋Ÿฐ ๋ฐฉ์‹์€ ์žฌ๊ท€์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ณ‘๋ ฌํ™”ํ•˜๋Š” ๋ฐ ๋งค์šฐ ํšจ๊ณผ์ ์ด์•ผ! ๐Ÿงฉ

์žฌ๋Šฅ๋„ท์—์„œ ์ด๋Ÿฐ ๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์˜ˆ์ œ์™€ ํŠœํ† ๋ฆฌ์–ผ์„ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์–ด. C++ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ „๋ฌธ๊ฐ€๋“ค์ด ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค์–‘ํ•œ ํŒจํ„ด๊ณผ ๊ธฐ๋ฒ•์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์ง€! ๐ŸŽ“

9. ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ๊ณผ ๋””๋ฒ„๊น… ์ „๋žต ๐Ÿ”ง

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

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

1. ์ ์ ˆํ•œ ์Šค๋ ˆ๋“œ ์ˆ˜ ์„ ํƒ

ํ•˜๋“œ์›จ์–ด ์ฝ”์–ด ์ˆ˜์— ๋งž๊ฒŒ ์Šค๋ ˆ๋“œ ์ˆ˜๋ฅผ ์กฐ์ •ํ•ด. ์ผ๋ฐ˜์ ์œผ๋กœ std::thread::hardware_concurrency()๊ฐ€ ์ข‹์€ ์‹œ์ž‘์ ์ด์•ผ. ๋„ˆ๋ฌด ๋งŽ์€ ์Šค๋ ˆ๋“œ๋Š” ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ฆ๊ฐ€์‹œ์ผœ.

2. ์ž‘์—… ํฌ๊ธฐ ์ตœ์ ํ™” (๊ทธ๋ž˜๋‰ผ๋ž˜๋ฆฌํ‹ฐ)

๋„ˆ๋ฌด ์ž‘์€ ์ž‘์—…์€ ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์ปค์ง€๊ณ , ๋„ˆ๋ฌด ํฐ ์ž‘์—…์€ ๋ถ€ํ•˜ ๋ถˆ๊ท ํ˜•์„ ์ดˆ๋ž˜ํ•ด. ์ ์ ˆํ•œ ์ž‘์—… ํฌ๊ธฐ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด.

3. ๋ฐ์ดํ„ฐ ์ง€์—ญ์„ฑ ๊ณ ๋ ค

CPU ์บ์‹œ ํšจ์œจ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ ์ง€์—ญ์„ฑ(locality)์„ ๊ณ ๋ คํ•ด. ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์—ฐ์†๋œ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•ด.

4. ๋ฝ ๊ฒฝํ•ฉ ์ตœ์†Œํ™”

๋ฝ์€ ๋ณ‘๋ ฌ ์„ฑ๋Šฅ์˜ ๊ฐ€์žฅ ํฐ ๋ณ‘๋ชฉ ์ค‘ ํ•˜๋‚˜์•ผ. ๋ฝ ๋ฒ”์œ„๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ , ๊ฐ€๋Šฅํ•˜๋ฉด ๋ฝ ์—†๋Š”(lock-free) ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ์›์ž์  ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•ด.

5. ๊ฑฐ์ง“ ๊ณต์œ (False Sharing) ๋ฐฉ์ง€

์„œ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์บ์‹œ ๋ผ์ธ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์บ์‹œ ๋ฌดํšจํ™”๊ฐ€ ๋ฐœ์ƒํ•ด. ํŒจ๋”ฉ(padding)์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์žฌ๊ตฌ์„ฑํ•ด ๊ฑฐ์ง“ ๊ณต์œ ๋ฅผ ๋ฐฉ์ง€ํ•ด.

6. ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ ํ™œ์šฉ

Intel VTune, Valgrind, perf ๊ฐ™์€ ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณ‘๋ชฉ ์ง€์ ์„ ์ฐพ๊ณ  ์ตœ์ ํ™”ํ•ด.

๊ฑฐ์ง“ ๊ณต์œ (False Sharing) ์˜ˆ์‹œ์™€ ํ•ด๊ฒฐ์ฑ… ๐Ÿ”„

// ๊ฑฐ์ง“ ๊ณต์œ  ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ์ฝ”๋“œ
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>

struct Counter {
    int value1;  // ์Šค๋ ˆ๋“œ 1์ด ์‚ฌ์šฉ
    int value2;  // ์Šค๋ ˆ๋“œ 2๊ฐ€ ์‚ฌ์šฉ
};

void increment_with_false_sharing(Counter& counter, int thread_id, int iterations) {
    if (thread_id == 0) {
        for (int i = 0; i < iterations; ++i) {
            counter.value1++;
        }
    } else {
        for (int i = 0; i < iterations; ++i) {
            counter.value2++;
        }
    }
}

// ๊ฑฐ์ง“ ๊ณต์œ  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ ์ฝ”๋“œ
struct PaddedCounter {
    int value1;
    char padding1[60];  // ์บ์‹œ ๋ผ์ธ ํฌ๊ธฐ(64๋ฐ”์ดํŠธ)๋ฅผ ๊ณ ๋ คํ•œ ํŒจ๋”ฉ
    int value2;
    char padding2[60];
};

void increment_without_false_sharing(PaddedCounter& counter, int thread_id, int iterations) {
    if (thread_id == 0) {
        for (int i = 0; i < iterations; ++i) {
            counter.value1++;
        }
    } else {
        for (int i = 0; i < iterations; ++i) {
            counter.value2++;
        }
    }
}

int main() {
    const int iterations = 100000000;
    
    // ๊ฑฐ์ง“ ๊ณต์œ  ์žˆ๋Š” ๋ฒ„์ „ ํ…Œ์ŠคํŠธ
    Counter counter = {0, 0};
    
    auto start = std::chrono::high_resolution_clock::now();
    
    std::thread t1(increment_with_false_sharing, std::ref(counter), 0, iterations);
    std::thread t2(increment_with_false_sharing, std::ref(counter), 1, iterations);
    
    t1.join();
    t2.join();
    
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> false_sharing_time = end - start;
    
    // ๊ฑฐ์ง“ ๊ณต์œ  ์—†๋Š” ๋ฒ„์ „ ํ…Œ์ŠคํŠธ
    PaddedCounter padded_counter = {0, {}, 0, {}};
    
    start = std::chrono::high_resolution_clock::now();
    
    std::thread t3(increment_without_false_sharing, std::ref(padded_counter), 0, iterations);
    std::thread t4(increment_without_false_sharing, std::ref(padded_counter), 1, iterations);
    
    t3.join();
    t4.join();
    
    end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> no_false_sharing_time = end - start;
    
    std::cout << "๊ฑฐ์ง“ ๊ณต์œ  ์žˆ๋Š” ๋ฒ„์ „ ์‹œ๊ฐ„: " << false_sharing_time.count() << "์ดˆ" << std::endl;
    std::cout << "๊ฑฐ์ง“ ๊ณต์œ  ์—†๋Š” ๋ฒ„์ „ ์‹œ๊ฐ„: " << no_false_sharing_time.count() << "์ดˆ" << std::endl;
    std::cout << "์„ฑ๋Šฅ ํ–ฅ์ƒ: " << false_sharing_time.count() / no_false_sharing_time.count() << "๋ฐฐ" << std::endl;
    
    return 0;
}

์œ„ ์ฝ”๋“œ๋Š” ๊ฑฐ์ง“ ๊ณต์œ (False Sharing) ๋ฌธ์ œ์™€ ๊ทธ ํ•ด๊ฒฐ์ฑ…์„ ๋ณด์—ฌ์ค˜. ๊ฑฐ์ง“ ๊ณต์œ ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์บ์‹œ ๋ผ์ธ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์„ฑ๋Šฅ ์ €ํ•˜ ํ˜„์ƒ์ด์•ผ. ํŒจ๋”ฉ์„ ์ถ”๊ฐ€ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅธ ์บ์‹œ ๋ผ์ธ์— ๋ฐฐ์น˜ํ•˜๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ”

๊ฑฐ์ง“ ๊ณต์œ (False Sharing) ๋ฌธ์ œ CPU ์ฝ”์–ด 1 CPU ์ฝ”์–ด 2 ์บ์‹œ ๋ผ์ธ (64 ๋ฐ”์ดํŠธ) value1 value2 ์ˆ˜์ • ์ˆ˜์ • ์บ์‹œ ๋ฌดํšจํ™” ๋ฐœ์ƒ!

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๋””๋ฒ„๊น… ์ „๋žต ๐Ÿ›

1. ๋กœ๊น… ํ™œ์šฉ

์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•œ ๋กœ๊น… ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ตฌํ˜„ํ•ด ๊ฐ ์Šค๋ ˆ๋“œ์˜ ํ™œ๋™์„ ์ถ”์ ํ•ด. ๋กœ๊ทธ์— ํƒ€์ž„์Šคํƒฌํ”„์™€ ์Šค๋ ˆ๋“œ ID๋ฅผ ํฌํ•จ์‹œ์ผœ.

2. ๋‹จ์ˆœํ™” ๋ฐ ์ ์ง„์  ๋ณต์žกํ™”

๋ฌธ์ œ๋ฅผ ๋‹จ์ˆœํ•œ ์ผ€์ด์Šค๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด ์ ์ง„์ ์œผ๋กœ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๊ฐ€๋ฉฐ ๋””๋ฒ„๊น…ํ•ด. ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ๋จผ์ € ํ…Œ์ŠคํŠธํ•œ ํ›„ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ๋กœ ํ™•์žฅํ•ด.

3. ํŠน์ˆ˜ ๋””๋ฒ„๊น… ๋„๊ตฌ ํ™œ์šฉ

Valgrind์˜ Helgrind, Intel Inspector, Thread Sanitizer ๊ฐ™์€ ์Šค๋ ˆ๋“œ ๋””๋ฒ„๊น… ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•ด ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค์™€ ๊ต์ฐฉ ์ƒํƒœ๋ฅผ ๊ฐ์ง€ํ•ด.

4. ๊ฒฐ์ •์  ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ๋งŒ๋“ค๊ธฐ

๋น„๊ฒฐ์ •์  ๋™์‹œ์„ฑ ๋ฒ„๊ทธ๋ฅผ ๊ฒฐ์ •์ ์œผ๋กœ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋งŒ๋“ค์–ด. ๋™๊ธฐํ™” ์ง€์ ์„ ์ถ”๊ฐ€ํ•ด ํŠน์ • ์ธํ„ฐ๋ฆฌ๋น™์„ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์–ด.

5. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ฐ ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ

๋™์‹œ์„ฑ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์ฒ ์ €ํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ์žฅ์‹œ๊ฐ„ ์‹คํ–‰๋˜๋Š” ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ๋กœ ๊ฐ„ํ—์  ๋ฒ„๊ทธ๋ฅผ ์ฐพ์•„๋‚ด.

์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•œ ๋กœ๊น… ๊ตฌํ˜„ ์˜ˆ์‹œ ๐Ÿ“

#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <chrono>
#include <iomanip>
#include <sstream>

class ThreadSafeLogger {
public:
    enum LogLevel {
        DEBUG,
        INFO,
        WARNING,
        ERROR
    };
    
    ThreadSafeLogger(const std::string& filename) : file(filename, std::ios::app) {
        if (!file.is_open()) {
            throw std::runtime_error("๋กœ๊ทธ ํŒŒ์ผ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: " + filename);
        }
    }
    
    ~ThreadSafeLogger() {
        if (file.is_open()) {
            file.close();
        }
    }
    
    void log(LogLevel level, const std::string& message) {
        std::lock_guard<std::mutex> lock(mutex);
        
        auto now = std::chrono::system_clock::now();
        auto now_time_t = std::chrono::system_clock::to_time_t(now);
        auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
            now.time_since_epoch()) % 1000;
        
        std::stringstream ss;
        ss << std::put_time(std::localtime(&now_time_t), "%Y-%m-%d %H:%M:%S");
        ss << '.' << std::setfill('0') << std::setw(3) << now_ms.count();
        
        file << ss.str() << " ["
             << std::this_thread::get_id() << "] "
             << levelToString(level) << ": "
             << message << std::endl;
    }
    
    void debug(const std::string& message) {
        log(DEBUG, message);
    }
    
    void info(const std::string& message) {
        log(INFO, message);
    }
    
    void warning(const std::string& message) {
        log(WARNING, message);
    }
    
    void error(const std::string& message) {
        log(ERROR, message);
    }
    
private:
    std::ofstream file;
    std::mutex mutex;
    
    std::string levelToString(LogLevel level) {
        switch (level) {
            case DEBUG:   return "DEBUG";
            case INFO:    return "INFO";
            case WARNING: return "WARNING";
            case ERROR:   return "ERROR";
            default:      return "UNKNOWN";
        }
    }
};

// ์‚ฌ์šฉ ์˜ˆ์‹œ
void worker_function(ThreadSafeLogger& logger, int id) {
    logger.info("์ž‘์—…์ž " + std::to_string(id) + " ์‹œ์ž‘");
    
    // ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    for (int i = 0; i < 5; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100 * id));
        logger.debug("์ž‘์—…์ž " + std::to_string(id) + " ๋‹จ๊ณ„ " + std::to_string(i) + " ์‹คํ–‰ ์ค‘");
    }
    
    if (id % 3 == 0) {
        logger.warning("์ž‘์—…์ž " + std::to_string(id) + " ๊ฒฝ๊ณ  ์ƒํ™ฉ ๋ฐœ์ƒ");
    }
    
    logger.info("์ž‘์—…์ž " + std::to_string(id) + " ์™„๋ฃŒ");
}

int main() {
    try {
        ThreadSafeLogger logger("multithreading_log.txt");
        
        logger.info("ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘");
        
        std::vector<std::thread> threads;
        
        // ์—ฌ๋Ÿฌ ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
        for (int i = 0; i < 5; ++i) {
            threads.emplace_back(worker_function, std::ref(logger), i);
        }
        
        // ๋ชจ๋“  ์Šค๋ ˆ๋“œ ์™„๋ฃŒ ๋Œ€๊ธฐ
        for (auto& t : threads) {
            t.join();
        }
        
        logger.info("๋ชจ๋“  ์ž‘์—…์ž ์™„๋ฃŒ");
        logger.info("ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ");
    }
    catch (const std::exception& e) {
        std::cerr << "์˜ค๋ฅ˜ ๋ฐœ์ƒ: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

์œ„ ์ฝ”๋“œ๋Š” ์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•œ ๋กœ๊น… ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์˜ˆ์‹œ์•ผ. ๋ฎคํ…์Šค๋ฅผ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ๋กœ๊ทธ๋ฅผ ์ž‘์„ฑํ•ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด. ํƒ€์ž„์Šคํƒฌํ”„์™€ ์Šค๋ ˆ๋“œ ID๋ฅผ ํฌํ•จํ•ด ๋กœ๊ทธ๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์—ˆ์ง€! ๐Ÿ“Š

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0