Ver2.0 ๐ C++๋ก ๋ฐฐ์ฐ๋ ๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋ชจ๋ ๊ฒ ๐

๐ C++๋ก ๋ฐฐ์ฐ๋ ๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋ชจ๋ ๊ฒ ๐
์๋ , ์ฝ๋ฉ ์น๊ตฌ๋ค! ์ค๋์ ํ๋ก๊ทธ๋จ์ ์ฑ๋ฅ์ ํญ๋ฐ์ ์ผ๋ก ํฅ์์ํค๋ ๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋ํด ํจ๊ป ์์๋ณผ ๊ฑฐ์ผ. ๋ณต์กํด ๋ณด์ด์ง๋ง ๊ฑฑ์ ๋ง! ์น์ ํ๊ฒ ์ค๋ช ํด ์ค๊ฒ. ๐
๐ ๋ชฉ์ฐจ
- ๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ์ ๊ธฐ๋ณธ ๊ฐ๋
- C++์์์ ์ค๋ ๋ ์์ฑ๊ณผ ๊ด๋ฆฌ
- ๋๊ธฐํ ๋ฉ์ปค๋์ฆ: ๋ฎคํ ์ค, ์ธ๋งํฌ์ด, ์กฐ๊ฑด ๋ณ์
- ๋ฐ์ดํฐ ๋ ์ด์ค์ ๊ต์ฐฉ ์ํ(Deadlock) ์ดํดํ๊ธฐ
- C++11/14/17/20์ ๋์์ฑ ๊ธฐ๋ฅ๋ค
- ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ๊ณผ Future/Promise
- ์ค๋ ๋ ํ๊ณผ ์์ ํ ๊ตฌํํ๊ธฐ
- ์ค์ ์์ : ๋ณ๋ ฌ ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ
- ์ฑ๋ฅ ์ต์ ํ ํ๊ณผ ๋๋ฒ๊น ์ ๋ต
- ๋ฉํฐ์ค๋ ๋ฉ์ ๋ฏธ๋์ ์ต์ ํธ๋ ๋
1. ๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ์ ๊ธฐ๋ณธ ๊ฐ๋ ๐งฉ
์ปดํจํฐ ํ๋ก๊ทธ๋จ์ด ์คํ๋ ๋, ๊ธฐ๋ณธ์ ์ผ๋ก๋ ํ ๋ฒ์ ํ๋์ ์์ ๋ง ์ฒ๋ฆฌํด. ์ด๊ฑธ ์ฑ๊ธ ์ค๋ ๋ ์คํ์ด๋ผ๊ณ ๋ถ๋ฅด์ง. ๊ทผ๋ฐ ์์ฆ ์ปดํจํฐ๋ ์ฌ๋ฌ ๊ฐ์ ์ฝ์ด๋ฅผ ๊ฐ์ง๊ณ ์์์? ์ด ๋ชจ๋ ์ฝ์ด๋ฅผ ํ์ฉํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น? ๋ฐ๋ก ์ฌ๊ธฐ์ ๋ฉํฐ์ค๋ ๋ฉ์ด ๋ฑ์ฅํ๋ ๊ฑฐ์ผ! ๐ฎ
์ค๋ ๋(Thread)๋ ๋ญ๊น? ๐ค
์ค๋ ๋๋ ํ๋ก์ธ์ค ๋ด์์ ์คํ๋๋ ์์ ์ ๊ฐ์ฅ ์์ ๋จ์์ผ. ํ๋์ ํ๋ก์ธ์ค๋ ์ฌ๋ฌ ๊ฐ์ ์ค๋ ๋๋ฅผ ๊ฐ์ง ์ ์๊ณ , ์ด ์ค๋ ๋๋ค์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๊ณต์ ํ๋ฉด์ ๋์์ ๋ค๋ฅธ ์์ ์ ์ํํ ์ ์์ด.
๋์์ฑ(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;
}
๋๋ค ํจ์๋ก ์ค๋ ๋ ์์ฑํ๊ธฐ ๐ฏ
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๋ ์์ฑ๋ ๋ ๋ฎคํ
์ค๋ฅผ ์๋์ผ๋ก ์ ๊ทธ๊ณ , ์๋ฉธ๋ ๋ ์๋์ผ๋ก ํด์ ํด. ์ด๋ ๊ฒ ํ๋ฉด ์์ธ๊ฐ ๋ฐ์ํด๋ ๋ฎคํ
์ค๊ฐ ์์ ํ๊ฒ ํด์ ๋๋ฏ๋ก ๋ฐ๋๋ฝ์ ๋ฐฉ์งํ ์ ์์ด! ๐
์กฐ๊ฑด ๋ณ์(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)์ด์ง ์์. ์ฆ, ์ฝ๊ธฐ-์์ -์ฐ๊ธฐ ๋จ๊ณ๋ก ๋๋๋๋ฐ, ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ด ์์
์ ์ํํ๋ฉด ์ผ๋ถ ์ฆ๊ฐ ์ฐ์ฐ์ด ์์ค๋ ์ ์์ด. ๐ฑ
๋ฐ์ดํฐ ๋ ์ด์ค ํด๊ฒฐ ๋ฐฉ๋ฒ ๐ก
- ๋ฎคํ ์ค ์ฌ์ฉ: ๊ณต์ ๋ฐ์ดํฐ ์ ๊ทผ ์ ๋ฎคํ ์ค๋ก ๋ณดํธ
- ์์์ ์ฐ์ฐ ์ฌ์ฉ:
std::atomicํ์ ํ์ฉ - ์ค๋ ๋ ๋ก์ปฌ ์ ์ฅ์: ๊ฐ ์ค๋ ๋๊ฐ ๋ ๋ฆฝ์ ์ธ ๋ฐ์ดํฐ ์ฌ์ฉ
- ๋ฝ ์๋ ์๋ฃ๊ตฌ์กฐ: ํน๋ณํ ์ค๊ณ๋ ๋์์ฑ ์๋ฃ๊ตฌ์กฐ ํ์ฉ
์์์ ์ฐ์ฐ์ ์ฌ์ฉํ ์์๋ฅผ ์ดํด๋ณผ๊น?
#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์ ์ ๊ทธ๋ ค ํด. ์ด๋ก ์ธํด ์๋ก๊ฐ ์๋ก์ ๋ฎคํ ์ค๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๊ต์ฐฉ ์ํ๊ฐ ๋ฐ์ํ ์ ์์ด! ๐ฑ
๊ต์ฐฉ ์ํ ๋ฐฉ์ง ๋ฐฉ๋ฒ ๐ก
- ๋ฝ ์์ ์ผ๊ด์ฑ ์ ์ง: ํญ์ ๊ฐ์ ์์๋ก ๋ฎคํ ์ค ์ ๊ธ
- std::lock ์ฌ์ฉ: ์ฌ๋ฌ ๋ฎคํ ์ค๋ฅผ ํ ๋ฒ์ ์์ ํ๊ฒ ์ ๊ธ
- ๋ฝ ํ์์์ ์ค์ : ์ผ์ ์๊ฐ ํ ๋ฝ ํ๋ ํฌ๊ธฐ
- ๊ต์ฐฉ ์ํ ๊ฐ์ง: ๋๋ฒ๊น ๋๊ตฌ๋ก ๊ต์ฐฉ ์ํ ๊ฐ์ง
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()์ด ํธ์ถ๋๋ฏ๋ก ์์ธ ๋ฐ์ ์์๋ ์์ ํด. ๐
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์์ ๊ฐ์ ๊ฐ์ ธ์. ์ด๋ ๊ฒ ํ๋ฉด ๋ ์ค๋ ๋ ๊ฐ์ ๊ฐ์ ์์ ํ๊ฒ ์ ๋ฌํ ์ ์์ด! ๐
์์ธ ์ฒ๋ฆฌ์ 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๋ฅผ ๋ฐํํด์ ๊ฒฐ๊ณผ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐ์ ์ ์์ด! ๐
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;
}
์ ์ฝ๋๋ ๋ณ๋ ฌ ๋งต-๋ฆฌ๋์ค ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ์์์ผ. ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ ์ฒญํฌ๋ก ๋๋๊ณ , ๊ฐ ์ฒญํฌ๋ฅผ ๋ณ๋์ ์ค๋ ๋์์ ์ฒ๋ฆฌํ ๋ค์, ๊ฒฐ๊ณผ๋ฅผ ํฉ์ณ. ์ด ๋ฐฉ์์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ์ ๋ณ๋ ฌํํ๋ ๊ฐ๋ ฅํ ํจํด์ด์ผ! ๐
๋ณ๋ ฌ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๐ผ๏ธ
์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ ํฝ์ ๋จ์ ์ฐ์ฐ์ด ๋ง์ ๋ณ๋ ฌํ์ ๋งค์ฐ ์ ํฉํด. ๊ฐ๋จํ ์ด๋ฏธ์ง ๋ธ๋ฌ ํํฐ๋ฅผ ๋ณ๋ ฌ๋ก ๊ตฌํํด๋ณด์:
#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) ๋ฌธ์ ์ ๊ทธ ํด๊ฒฐ์ฑ ์ ๋ณด์ฌ์ค. ๊ฑฐ์ง ๊ณต์ ๋ ์๋ก ๋ค๋ฅธ ์ค๋ ๋๊ฐ ๊ฐ์ ์บ์ ๋ผ์ธ์ ์๋ ๋ฐ์ดํฐ๋ฅผ ์์ ํ ๋ ๋ฐ์ํ๋ ์ฑ๋ฅ ์ ํ ํ์์ด์ผ. ํจ๋ฉ์ ์ถ๊ฐํด ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฅธ ์บ์ ๋ผ์ธ์ ๋ฐฐ์นํ๋ฉด ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ด! ๐
๋ฉํฐ์ค๋ ๋ฉ ๋๋ฒ๊น ์ ๋ต ๐
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๋ฅผ ํฌํจํด ๋ก๊ทธ๋ฅผ ์ถ์ ํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค์์ง! ๐
10. ๋ฉํฐ์ค๋ ๋ฉ์ ๋ฏธ๋์ ์ต์ ํธ๋ ๋ ๐ฎ
๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. ์ต์ ํธ๋ ๋์ ๋ฏธ๋ ๋ฐฉํฅ์ ์ดํด๋ณด๋ฉด์ ๊ธ์ ๋ง๋ฌด๋ฆฌํด๋ณผ๊ฒ! ๐
C++20 ์ฝ๋ฃจํด(Coroutines) ๐งต
C++20์์ ๊ฐ์ฅ ํฅ๋ฏธ๋ก์ด ๊ธฐ๋ฅ ์ค ํ๋๋ ์ฝ๋ฃจํด(Coroutines)์ด์ผ. ์ฝ๋ฃจํด์ ํจ์์ ์คํ์ ์ค๊ฐ์ ์ผ์ ์ค๋จํ๊ณ ๋์ค์ ๋ค์ ์ฌ๊ฐํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํด. ์ด๋ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ํจ์ฌ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ค์ด์ค!
#include <iostream>
#include <coroutine>
#include <thread>
#include <chrono>
// ๊ฐ๋จํ ์ฝ๋ฃจํด ๊ตฌํ์ ์ํ future ํ์
template<typename T>
struct simple_future {
struct promise_type {
T value;
simple_future get_return_object() {
return simple_future(std::coroutine_handle<promise_type>::from_promise(*this));
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_value(T v) {
value = v;
}
void unhandled_exception() {
std::terminate();
}
};
std::coroutine_handle<promise_type> handle;
simple_future(std::coroutine_handle<promise_type> h) : handle(h) {}
~simple_future() {
if (handle) handle.destroy();
}
T get() {
return handle.promise().value;
}
};
// ๋น๋๊ธฐ ์์
์ ์๋ฎฌ๋ ์ด์
ํ๋ awaiter
struct Awaiter {
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<void> h) {
std::thread([h]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
h.resume();
}).detach();
}
void await_resume() {}
};
// ์ฝ๋ฃจํด ํจ์
simple_future<int> async_task() {
std::cout << "์ฝ๋ฃจํด ์์" << std::endl;
// ์ฒซ ๋ฒ์งธ ๋น๋๊ธฐ ์์
co_await Awaiter{};
std::cout << "์ฒซ ๋ฒ์งธ ์์
์๋ฃ" << std::endl;
// ๋ ๋ฒ์งธ ๋น๋๊ธฐ ์์
co_await Awaiter{};
std::cout << "๋ ๋ฒ์งธ ์์
์๋ฃ" << std::endl;
// ๊ฒฐ๊ณผ ๋ฐํ
co_return 42;
}
int main() {
std::cout << "๋ฉ์ธ ํจ์ ์์" << std::endl;
auto future = async_task();
std::cout << "์ฝ๋ฃจํด ํธ์ถ ํ ๋ฉ์ธ ํจ์ ๊ณ์ ์คํ" << std::endl;
// ๋ฉ์ธ ์ค๋ ๋์์ ๋ค๋ฅธ ์์
์ํ
std::this_thread::sleep_for(std::chrono::seconds(3));
int result = future.get();
std::cout << "์ฝ๋ฃจํด ๊ฒฐ๊ณผ: " << result << std::endl;
return 0;
}
์ฝ๋ฃจํด์ ๋น๋๊ธฐ ์ฝ๋๋ฅผ ๋๊ธฐ ์ฝ๋์ฒ๋ผ ์์ฑํ ์ ์๊ฒ ํด์ค. co_await๋ฅผ ์ฌ์ฉํ๋ฉด ๋น๋๊ธฐ ์์
์ด ์๋ฃ๋ ๋๊น์ง ์ฝ๋ฃจํด์ ์ผ์ ์ค๋จํ๊ณ , ์๋ฃ๋๋ฉด ์๋์ผ๋ก ๋ค์ ์คํ๋ผ. ์ด๋ ์ฝ๋ฐฑ ์ง์ฅ์ ํผํ๊ณ ์ฝ๋ ๊ฐ๋
์ฑ์ ํฌ๊ฒ ํฅ์์์ผ! ๐
๋ณ๋ ฌ ํ๋ก๊ทธ๋๋ฐ ๋ชจ๋ธ์ ๋ฐ์ ๐
1. ์์ ๊ธฐ๋ฐ ๋ณ๋ ฌ ์ฒ๋ฆฌ
์ค๋ ๋๋ฅผ ์ง์ ๊ด๋ฆฌํ๋ ๋์ ์์ (Task) ๋จ์๋ก ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๋ ๋ฐฉ์์ด ๋๊ณ ์์ด. C++23์์๋ ์ด๋ฅผ ์ํ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ง์์ด ๊ฐํ๋ ์์ ์ด์ผ.
2. ๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ
๋ฐ์ดํฐ ์คํธ๋ฆผ๊ณผ ๋ณํ ์ ํ์ ๊ธฐ๋ฐํ ๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ์ด ์ธ๊ธฐ๋ฅผ ์ป๊ณ ์์ด. RxCpp ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ด๋ฅผ ์ง์ํด.
3. ์กํฐ ๋ชจ๋ธ
์กํฐ ๋ชจ๋ธ์ ๋ฉ์์ง ์ ๋ฌ ๊ธฐ๋ฐ์ ๋์์ฑ ๋ชจ๋ธ๋ก, ๊ณต์ ์ํ ์์ด ๋ ๋ฆฝ์ ์ธ ์กํฐ๋ค์ด ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์ผ๋ฉฐ ๋์ํด. C++์์๋ CAF(C++ Actor Framework) ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ด.
4. GPGPU ์ปดํจํ
CUDA, OpenCL, SYCL ๋ฑ์ ํตํ GPU ๊ธฐ๋ฐ ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. C++์์๋ ์ด๋ฌํ ๊ธฐ์ ๊ณผ์ ํตํฉ์ด ๊ฐํ๋๊ณ ์์ง.
ํ๋์จ์ด ํธ๋ ๋์ ์ํฅ ๐ฅ๏ธ
ํ๋์จ์ด ๋ฐ์ ๋ฐฉํฅ๋ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ํฐ ์ํฅ์ ๋ฏธ์น๊ณ ์์ด:
1. ์ด๊ธฐ์ข ์ปดํจํ
CPU, GPU, FPGA, ์ ์ฉ ๊ฐ์๊ธฐ ๋ฑ ๋ค์ํ ํ๋ก์ธ์๋ฅผ ํจ๊ป ํ์ฉํ๋ ์ด๊ธฐ์ข ์ปดํจํ ์ด ์ค์ํด์ง๊ณ ์์ด. C++์์๋ ์ด๋ฅผ ์ง์ํ๊ธฐ ์ํ ํ์คํ ๋ ธ๋ ฅ์ด ์งํ ์ค์ด์ผ.
2. ๋ฉ๋ชจ๋ฆฌ ๊ณ์ธต ๊ตฌ์กฐ์ ๋ณต์กํ
NUMA(Non-Uniform Memory Access) ์ํคํ ์ฒ์ ๋ณต์กํ ์บ์ ๊ณ์ธต ๊ตฌ์กฐ๋ก ์ธํด ๋ฉ๋ชจ๋ฆฌ ์ง์ญ์ฑ์ด ๋์ฑ ์ค์ํด์ง๊ณ ์์ด. ์ด๋ฅผ ๊ณ ๋ คํ ์๊ณ ๋ฆฌ์ฆ ์ค๊ณ๊ฐ ํ์ํด.
3. ์ ์ ๋ ฅ ์ปดํจํ
๋ชจ๋ฐ์ผ ๊ธฐ๊ธฐ์ ๋ฐ์ดํฐ ์ผํฐ์์ ์๋์ง ํจ์จ์ฑ์ด ์ค์ํด์ง๋ฉด์, ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ๋ ์ ๋ ฅ ์๋น๋ฅผ ๊ณ ๋ คํด์ผ ํด. ํ์ํ ๋๋ง ์ฝ์ด๋ฅผ ํ์ฑํํ๋ ๋ฑ์ ์ ๋ต์ด ์ค์ํด์ง๊ณ ์์ด.
C++23๊ณผ ๊ทธ ์ดํ์ ๋์์ฑ ๊ธฐ๋ฅ ๐
C++23๊ณผ ํฅํ ๋ฒ์ ์์ ๊ธฐ๋๋๋ ๋์์ฑ ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ด์ผ:
1. ํฅ์๋ executor์ sender/receiver
๋น๋๊ธฐ ์์ ์ ์คํ๊ณผ ์กฐํฉ์ ์ํ ํ์คํ๋ ํ๋ ์์ํฌ๊ฐ ๊ฐ๋ฐ ์ค์ด์ผ. ์ด๋ ์์ ์ค์ผ์ค๋ง๊ณผ ๋น๋๊ธฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ์ ๋ ์ฝ๊ฒ ๋ง๋ค์ด์ค ๊ฑฐ์ผ.
2. ๊ฐ์ ๋ ์ฝ๋ฃจํด ์ง์
C++20์์ ๋์ ๋ ์ฝ๋ฃจํด์ด ๋์ฑ ์ฌ์ฉํ๊ธฐ ์ฝ๊ณ ํจ์จ์ ์ผ๋ก ๊ฐ์ ๋ ์์ ์ด์ผ. ํนํ ์ ๋ค๋ ์ดํฐ์ ๋น๋๊ธฐ ์คํธ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ง์์ด ๊ฐํ๋ ๊ฑฐ์ผ.
3. ๋ณ๋ ฌ ์๊ณ ๋ฆฌ์ฆ ํ์ฅ
๋ ๋ง์ ํ์ค ์๊ณ ๋ฆฌ์ฆ์ด ๋ณ๋ ฌ ์คํ์ ์ง์ํ๊ณ , ์คํ ์ ์ฑ ๋ ๋ ์ธ๋ฐํ๊ฒ ์ ์ดํ ์ ์๊ฒ ๋ ๊ฑฐ์ผ. ๋ํ GPU ๊ฐ์ ์ด๊ธฐ์ข ์ฅ์น์์์ ์คํ๋ ํ์คํ๋ ์ ์์ด.
๋ง๋ฌด๋ฆฌ ๐ฏ
๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ํ๋ ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ํ์์ ์ธ ๊ธฐ์ ์ด ๋์์ด. C++์ ์ ์์ค ์ ์ด์ ๊ณ ์์ค ์ถ์ํ๋ฅผ ๋ชจ๋ ์ ๊ณตํ๋ ๊ฐ๋ ฅํ ์ธ์ด๋ก, ๋ค์ํ ๋์์ฑ ํจํด์ ๊ตฌํํ ์ ์์ง!
์ด ๊ธ์์ ๋ค๋ฃฌ ๋ด์ฉ๋ค์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๋ฉด์ ๊ฒฝํ์ ์์๊ฐ๊ธธ ๋ฐ๋ผ. ์ฒ์์๋ ๋ณต์กํด ๋ณด์ผ ์ ์์ง๋ง, ์ฐ์ต์ ํตํด ์ ์ ์ต์ํด์ง ๊ฑฐ์ผ. ์ฌ๋ฅ๋ท์์ ๋ ๋ง์ C++ ๋ฉํฐ์ค๋ ๋ฉ ํํ ๋ฆฌ์ผ๊ณผ ์์ ๋ฅผ ์ฐพ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐
๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ์ธ๊ณ๋ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. ์๋ก์ด ๊ธฐ์ ๊ณผ ํจํด์ ๊พธ์คํ ํ์ตํ๊ณ , ์ฝ๋์ ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ์ง์์ ์ผ๋ก ๊ฐ์ ํด๋๊ฐ์. ๋ฉํฐ์ฝ์ด ์๋์ C++ ๊ฐ๋ฐ์๋ก์ ๊ฒฝ์๋ ฅ์ ์ ์งํ๋ ๋ฐ ํฐ ๋์์ด ๋ ๊ฑฐ์ผ! ๐ช
๋ฉํฐ์ค๋ ๋ฉ๊ณผ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋ง์คํฐํ๋ฉด, ๋น์ ์ C++ ํ๋ก๊ทธ๋จ์ ๋ ๋น ๋ฅด๊ณ , ๋ ๋ฐ์์ฑ์ด ์ข๊ณ , ๋ ํจ์จ์ ์ผ๋ก ๋ณํ ๊ฑฐ์ผ. ํ์ด์ ๋น์ด! ๐
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.