अंतिम अद्यतन:2026-10-12
C++ multithreading की शुरुआत¶
इस लेख का सारा code 《C++那些事》 repository में देखा जा सकता है।
1. Threads और processes को समझना¶
Thread scheduling की basic unit है, process resource allocation की basic unit है। एक program को process समझा जा सकता है, और process में कई threads शामिल होते हैं।
उदाहरण के लिए: browser एक process है, और हर खुला tab एक thread है।
दोनों का सरल अंतर:
- Address space और अन्य resources: processes एक-दूसरे से स्वतंत्र होते हैं, एक ही process के threads आपस में बाँटते हैं। किसी process का thread दूसरे process में दिखाई नहीं देता।
- संचार: Processes के बीच IPC, threads के बीच सीधे process के data segment (जैसे global variables) को पढ़कर-लिखकर संवाद — data की consistency सुनिश्चित करने के लिए process synchronization और mutual exclusion के साधनों की सहायता चाहिए।
- Scheduling और switching: thread का context switching, process के context switching से कहीं अधिक तेज़ होता है।
- Multithreaded OS में, process एक executable entity नहीं होता।
IPC communication और thread communication पर आगे एक नया लेख आएगा।
2. Threads बनाने के पाँच तरीके¶
- Function pointer
- Lambda function
- Functor
- Non-static member function
- Static member function
2.1 Function pointer¶
// 1.函数指针
void fun(int x) {
while (x-- > 0) {
cout << x << endl;
}
}
// 调用
std::thread t1(fun, 10);
t1.join();
2.2 Lambda function¶
// 注意:如果我们创建多线程 并不会保证哪一个先开始
int main() {
// 2.Lambda函数
auto fun = [](int x) {
while (x-- > 0) {
cout << x << endl;
}
};
// std::1.thread t1(fun, 10);
// 也可以写成下面:
std::thread t1_1([](int x) {
while (x-- > 0) {
cout << x << endl;
}
}, 11);
// std::1.thread t2(fun, 10);
// t1.join();
t1_1.join();
// t2.join();
return 0;
}
2.3 Functor¶
// 3.functor (Funciton Object)
class Base {
public:
void operator()(int x) {
while (x-- > 0) {
cout << x << endl;
}
}
};
// 调用
thread t(Base(), 10);
t.join();
2.4 Non-static member function¶
// 4.Non-static member function
class Base {
public:
void fun(int x) {
while (x-- > 0) {
cout << x << endl;
}
}
};
// 调用
thread t(&Base::fun,&b, 10);
t.join();
2.5 Static member function¶
// 4.Non-static member function
class Base {
public:
static void fun(int x) {
while (x-- > 0) {
cout << x << endl;
}
}
};
// 调用
thread t(&Base::fun, 10);
t.join();
3. join और detach¶
3.1 join¶
- एक बार thread शुरू होने पर, thread के पूरा होने का इंतज़ार करने के लिए उस object पर join() call करना होता है
- Double join से program terminate हो जाता है
- join से पहले हमें check करना चाहिए कि join किया जा सकता है या नहीं, joinable() का उपयोग करके
void run(int count) {
while (count-- > 0) {
cout << count << endl;
}
std::this_thread::sleep_for(chrono::seconds(3));
}
int main() {
thread t1(run, 10);
cout << "main()" << endl;
t1.join();
if (t1.joinable()) {
t1.join();
}
cout << "main() after" << endl;
return 0;
}
3.2 detach¶
- यह नए बने thread को parent thread से अलग करने के लिए उपयोग होता है
- Thread को detach करने से पहले ज़रूर check करें कि वह joinable है या नहीं, वरना दो बार detach हो सकता है, और double detach() से program terminate हो जाता है
- अगर हमारा thread detached है और main function return कर रहा है, तो detached thread का निष्पादन suspend हो जाएगा
void run(int count) {
while (count-- > 0) {
cout << count << endl;
}
std::this_thread::sleep_for(chrono::seconds(3));
}
int main() {
thread t1(run, 10);
cout << "main()" << endl;
t1.detach();
if(t1.joinable())
t1.detach();
cout << "main() after" << endl;
return 0;
4. Critical section और mutex¶
4.1 Critical section क्या है?¶
Critical section code का एक हिस्सा है, जिसे program के सही चलने के लिए एक समय पर केवल एक ही thread execute कर सकता है। अगर दो threads (या processes) एक साथ critical section का code execute करें, तो program का व्यवहार सही नहीं रह सकता।
4.2 क्या केवल एक variable को increment करना critical section है?¶
शायद हो सकता है।
Variable increment (i++) की प्रक्रिया तीन steps में होती है:
- Memory की सामग्री CPU register में copy करना। load
- CPU में उस value को increment करना। increment
- नई value को memory में store करना। store
अगर उस memory location (जैसे नीचे वाला variable i) तक केवल एक thread ही पहुंच सकता है, तो कोई race condition नहीं होती, और i से जुड़ा कोई critical section नहीं होता। लेकिन sum variable एक global variable है, जिस तक दो threads पहुंच सकते हैं। दोनों threads एक साथ variable को increment करने की कोशिश कर सकते हैं।
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
int sum = 0; //shared
mutex m;
void *countgold() {
int i; //local to each thread
for (i = 0; i < 10000000; i++) {
sum += 1;
}
return NULL;
}
int main() {
thread t1(countgold);
thread t2(countgold);
//Wait for both threads to finish
t1.join();
t2.join();
cout << "sum = " << sum << endl;
return 0;
}
ऊपर वाले code का typical आउटपुट sum का कुल 20000000 होता है। Race condition के कारण program हर बार अलग कुल print करता है। यह code दो threads को एक साथ sum को पढ़ने-लिखने से नहीं रोकता। उदाहरण के लिए, दोनों threads sum की current value को अपने-अपने CPU में copy करते हैं (मान लीजिए 123)। दोनों threads अपनी copy को एक बढ़ाते हैं। दोनों threads वह value (124) वापस लिख देते हैं। अगर threads अलग-अलग समय पर sum तक पहुंचते, तो count 125 होता।
4.3 एक समय पर केवल एक thread का global variable तक पहुंचना कैसे सुनिश्चित करें?¶
अगर एक thread फिलहाल critical section में है, तो हम चाहते हैं कि दूसरा thread तब तक प्रतीक्षा करे जब तक पहला thread पूरा न हो जाए। इसके लिए हम mutex lock (mutual exclusion का संक्षिप्त रूप) उपयोग कर सकते हैं।
Mutex lock की सरल उपमा:

दूसरों के अंदर आने से रोकने का एक सरल तरीका है, दरवाज़े पर एक ताला लगाना। पहले पहुंचने वाला ताला लगा देता है, बाद में आने वाला ताला लगा देखकर दरवाज़े पर कतार में लग जाता है, और ताला खुलने पर अंदर जाता है। इसी को "mutex lock" कहते हैं (Mutual exclusion, संक्षेप में Mutex), जो कई threads को एक साथ किसी memory area को पढ़ने-लिखने से रोकता है।
ऊपर वाला code सही आउटपुट देगा: sum = 20000000।
References¶
https://www.youtube.com/watch?v=eZ8yKZo-PGw&list=PLk6CEY9XxSIAeK-EAh3hB4fgNvYkYmghp&index=4
संबंधित विषय¶
- C++ Concurrency in Action रीडिंग गाइड | C++那些事
- C++ deadlock जांच | C++那些事
- C++ SIMD vectorization | C++那些事
- C++17 parallel STL | C++那些事
- C++ False Sharing और cache line | C++那些事
- GDB multithread All-Stop डीबगिंग | C++那些事
- Helgrind concurrency error detection | C++那些事
- libev event loop का सिद्धांत | C++那些事
- C++ std::thread kernel implementation | C++那些事
- C++ std::thread और jthread | C++那些事