cpp multithreading - pjy0121/Wiki GitHub Wiki

C++ thread ์ƒ์„ฑ ๋ฐฉ๋ฒ•

โ‘  std::thread ์‚ฌ์šฉ โ‘ก std::jthread ์‚ฌ์šฉ โ‘ข std::async ์‚ฌ์šฉ

std::thread

: ๊ธฐ๋ณธ thread ๊ฐ์ฒด. ๋ฐ˜๋“œ์‹œ detach๋‚˜ join์„ ํ˜ธ์ถœํ•ด์•ผ ํ•จ

  • thread ๊ฐ์ฒด ์†Œ๋ฉธ ์‹œ joinableํ•œ ์ƒํƒœ(detach ๋˜๋Š” joinํ•˜์ง€ ์•Š์€ ์ƒํƒœ)๋ผ๋ฉด std::terminate()๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ํ”„๋กœ๊ทธ๋žจ์ด ๊ฐ•์ œ ์ข…๋ฃŒ๋จ
๋ฉ”์„œ๋“œ ์—ญํ•  ๋น„๊ณ 
join ์ƒ์„ฑ๋œ thread๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆผ
detach thread๋ฅผ ๋–ผ์–ด๋‚ด์–ด ๋…๋ฆฝ์ ์œผ๋กœ(๋ฐฑ๊ทธ๋ผ์šด๋“œ) ์‹คํ–‰ main thread๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์ด ์ข…๋ฃŒ๋˜๊ธฐ ๋•Œ๋ฌธ์— detach๋œ thread๋“ค๋„ ํ•จ๊ป˜ ์ข…๋ฃŒ๋˜๋ฏ€๋กœ ์ฃผ์˜ํ•ด์•ผ ํ•จ

std::jthread

: ์ค‘๋‹จ ๊ฐ€๋Šฅํ•˜๋ฉฐ joinํ•  ํ•„์š”๊ฐ€ ์—†๋Š” thread ๊ฐ์ฒด

std::thread๋ฅผ ์“ธ ๋•Œ ๋งค ๋ฒˆ join์„ ํ•ด์•ผ ํ•˜๋ฏ€๋กœ, thread ๊ฐ์ฒด๋ฅผ ๋ฉค๋ฒ„๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” class๋ฅผ ๋งŒ๋“ค๊ณ  ์†Œ๋ฉธ์ž์—์„œ joinํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ์‹์ด ๋งŽ์ด ์“ฐ์˜€๋˜ ๊ฒƒ์œผ๋กœ ๋ณด์ž„

ex)

#include <iostream>
#include <thread>

class ThreadRAII {
public:
    template<typename F, typename ... ARGS>
    explicit ThreadRAII(F&& f, ARGS&&... args) : t(std::forward<F>(f), std::forward<ARGS>(args)...) {}

    ~ThreadRAII() {
        if (t.joinable())
            t.join();
    }
    std::thread& get() { return t; }
private:
    std::thread t;
};

void func(int n) {
    std::cout << "n : " << n << std::endl;
}

int main(void) {
    ThreadRAII t(func, 1);
    return 0;
}

ํ•˜์ง€๋งŒ C++20๋ถ€ํ„ฐ๋Š” ์†Œ๋ฉธ์ž์—์„œ join์„ ํ˜ธ์ถœํ•ด์ฃผ๋Š” std::jthread๋ฅผ ์ง€์›ํ•จ

ex)

int main(void) {
    std::jthread t(func, 1);
    return 0;
}

๋˜ํ•œ jthread๋Š” Cooperatively Interruptible(ํ˜‘๋ ฅ์ ์œผ๋กœ ์ค‘๋‹จ ๊ฐ€๋Šฅ)ํ•˜๋‹ค๋Š” ํŠน์„ฑ์„ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์—, ์‹คํ–‰ํ•  ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ std::stop_token type์„ ๋ฐ›๋„๋ก ํ•˜๋ฉด ์ค‘๋‹จ ๊ฐ€๋Šฅํ•œ thread๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ. ๋‹ค๋งŒ ๋‹ค๋ฅธ thread์—์„œ ์•„๋ฌด ๋•Œ๋‚˜ ์š”์ฒญํ•œ๋‹ค๊ณ  ์ค‘๋‹จ๋˜์ง€๋Š” ์•Š๊ณ , ์ค‘๋‹จ ์ง€์ ์„ ๋ฏธ๋ฆฌ ์ง€์ •ํ•ด๋†”์•ผ ํ•จ

ex)

[TODO] request_stop()๊ณผ stop_requested()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ

std::async

: ๋น„๋™๊ธฐ thread ์ƒ์„ฑ ํ•จ์ˆ˜

  • caller๊ฐ€ callee์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™๊ธฐ ๋ฐฉ์‹์€ ๋‹จ์ˆœํ•˜์ง€๋งŒ callee์˜ ์ž‘์—…๊ณผ caller์˜ ๋‚จ์€ ์ž‘์—… ์‚ฌ์ด ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ์—†๋”๋ผ๋„ caller๊ฐ€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋ฏ€๋กœ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ์Œ => ๋„คํŠธ์›Œํฌ, ํŒŒ์ผ I/O, DB query, ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์—ฐ์‚ฐ, ์—ฌ๋Ÿฌ ๋…๋ฆฝ์  ์ž‘์—…์˜ ๋™์‹œ ์ฒ˜๋ฆฌ ๋“ฑ์—์„œ ๋น„๋™๊ธฐ ๋ฐฉ์‹์˜ ์ฒ˜๋ฆฌ๊ฐ€ ๋งŽ์ด ์‚ฌ์šฉ๋จ
  • ๋‚ด๋ถ€์ ์œผ๋กœ thread๋ฅผ ๋งŒ๋“ค์–ด ์ง€์ •ํ•œ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  future๋ฅผ ๋ฐ˜ํ™˜ํ•จ
  • ๊ธฐ๋ณธ์ ์œผ๋กœ thread pool์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ํšจ์œจ์ ์ž„
  • launch option์„ ํ†ตํ•ด thread์˜ ์‹คํ–‰ ์‹œ์ ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Œ

launch option

  • std::launch::async : ์ƒˆ๋กœ์šด thread๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํ•จ์ˆ˜๋ฅผ ์ฆ‰์‹œ ์‹คํ–‰
  • std::launch::deferred : ํ•จ์ˆ˜๋ฅผ ์ฆ‰์‹œ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ , get()์ด๋‚˜ wait()์ด ํ˜ธ์ถœ๋  ๋•Œ(๊ฒฐ๊ณผ๊ฐ€ ํ•„์š”ํ•œ ์ˆœ๊ฐ„)๊นŒ์ง€ ์ง€์—ฐ์‹œํ‚ด
  • std::launch::async | std::launch::deferred : ์‹คํ–‰ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜์—ฌ ์‹คํ–‰ (๊ธฐ๋ณธ๊ฐ’)
[TODO] launch option์„ ์‚ฌ์šฉํ•œ async ์˜ˆ์‹œ

[์ด์Šˆ] std::async ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ํ•  ์ 

async ํ•จ์ˆ˜์—์„œ return๋˜๋Š” future์˜ ์†Œ๋ฉธ์ž์—์„œ๋Š” get ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋ฏ€๋กœ ๋‹ค์Œ์„ ์ฃผ์˜ํ•ด์•ผ ํ•จ

  • return๊ฐ’์„ ๋ฐ›์•˜์ง€๋งŒ get()์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ํ•จ์ˆ˜์—์„œ return๋œ future ๊ฐ์ฒด๊ฐ€ ๋ณต์‚ฌ๋˜์–ด ๋ณ€์ˆ˜์— ์ €์žฅ๋œ ํ›„ caller ํ•จ์ˆ˜๊ฐ€ ๋๋‚  ๋•Œ ์†Œ๋ฉธ๋˜๋ฏ€๋กœ caller ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜์ง€ ์•Š๊ณ  thread์˜ ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆผ
int add(int a, int b) {
    cout << "start add " << endl;
    this_thread::sleep_for(chrono::seconds(3));
    cout << "end add" << endl;
    return a + b;
}

int main() {
    cout << "start main" << endl;
    future<int> ft = async(add, 10, 20);
    cout << "end main" << endl;
    return 0;
}

[์ถœ๋ ฅ]

start main end main start add end add

  • return๊ฐ’์„ ๋ฐ›์ง€ ์•Š๊ณ  ํ˜ธ์ถœํ•œ ๊ฒฝ์šฐ future๊ฐ€ ์ž„์‹œ ๊ฐ์ฒด๋กœ ์ƒ์„ฑ๋˜์–ด, ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ์ง€์ ์—์„œ ๋‹ค์Œ ์ฝ”๋“œ๋กœ ๋„˜์–ด๊ฐ€์ง€ ์•Š๊ณ  thread์˜ ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆผ. ์ด๋Ÿฐ ๋ถ€๋ถ„์ด ์žˆ์„ ๊ฒฝ์šฐ ๋ณดํ†ต ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ warning์„ ๋‚ด์คŒ
int main() {
    cout << "start main" << endl;
    future<int> ft = async(add, 10, 20);
    cout << "end main" << endl;
    return 0;
}

[์ถœ๋ ฅ]

start main start add end add end main

โš ๏ธ **GitHub.com Fallback** โš ๏ธ