विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ deadlock जांच

C++ में concurrent programming की deadlock समस्या की जांच कैसे करें?

हाल में Apache arrow में एक parallelism support करने वाला operator लिखा: nested loop join, और चूंकि parallelism जुड़ी है, यहाँ वही deadlock समस्या आती है जिसके बारे में अक्सर कहा जाता है, मान लीजिए आपको deadlock की समस्या मिल जाए, तो उसे debug और locate कैसे करें?

यही इस लेख का लक्ष्य है, आपको concurrent programming की deadlock detection और deadlock debugging समस्या में जल्दी महारत दिलाना, यह बिल्कुल सारगर्भित है, चलिए मुख्य विषय पर आते हैं।

1. परिचय

Deadlock को बेहतर ढंग से समझाने के लिए, हम एक program से शुरुआत करते हैं।

std::mutex gMutex;

int t2() {
  std::lock_guard<std::mutex> m(gMutex);
  return 0;
}

int t1() {
  std::lock_guard<std::mutex> l(gMutex);
  return t2();
}

विश्वास है इस program को देखकर सबको लगेगा कि इसमें समस्या है, deadlock हो गया! समस्या इस बात में है कि t1() function और t2() function दोनों में global mutex lock gMutex पर locking operation किया गया है, लेकिन t1() function ने lock करने के बाद t2() function को call किया, और t2() function के अंदर फिर से gMutex पर lock करने की कोशिश की गई है।

t1 का lock लग चुका है, लेकिन अभी release नहीं हुआ, t2 फिर से lock करने जा रहा है, दोनों इंतज़ार कर रहे हैं कि पहले कौन release करे, और infinite loop में घुस गए। वास्तविक projects में code यहाँ जितना सरल नहीं होता, बहुत जटिल होता है, उदाहरण के लिए: मैंने Apache arrow में जो code लिखा है वह ऐसा है:

Status OnBuildSideFinished(size_t thread_index) {
  std::lock_guard<std::mutex> guard(probe_side_mutex_);
  // do something
  accumulate_build_ready_ = true;
  return scheduler_->StartTaskGroup(thread_index,task_group_probe_,queued_batches_to_probe_.batch_count()); 
}

देखिए यह code ऊपर वाले परिदृश्य से कहीं ज़्यादा जटिल है, कम से कम 5 स्तरों के stacks nested हैं, बाकी code अभी चिपकाया भी नहीं गया, लेकिन ये दोनों अनिवार्यतः एक ही deadlock model हैं।

2. डीबगिंग

Deadlock model की व्याख्या के बाद, ऐसी समस्या मिलने पर locate कैसे करें?

यहाँ दो तरीके अपनाए जा सकते हैं, पहला — program सीधे run करें, और फिर gdb attach करें।

उदाहरण के लिए:

./a.out

फिर process number ढूंढने के बाद:

gdb -p xxx

इस समय हमें wait कर रहे threads मिल जाते हैं।

(gdb) info threads 
  Id   Target Id                                 Frame 
* 1    Thread 0x7ffff7fe2740 (LWP 32301) "a.out" 0x00007ffff7bc8017 in pthread_join () from /lib64/libpthread.so.0
  2    Thread 0x7ffff6fd0700 (LWP 32305) "a.out" 0x00007ffff7bcd54d in __lll_lock_wait () from /lib64/libpthread.so.0

फिर __lll_lock_wait का stack देखें, उदाहरण के लिए मैंने यहाँ thread 2 देखा, और stack देखकर t1 और t2 के line numbers मिल गए, सीधे पता चल गया कि कहाँ समस्या है, बहुत सहज!

इस तरीके के अलावा, सीधे gdb से program run भी किया जा सकता है, उस समय program अटक जाएगा, फिर ctrl + c से मारने के बाद भी ऊपर जैसी ही जानकारी मिलती है।

उदाहरण के लिए:

(gdb) r
Starting program: /home/light/a.out 
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Hello World!
[New Thread 0x7ffff6fd0700 (LWP 32305)]
^C
Thread 1 "a.out" received signal SIGINT, Interrupt.
0x00007ffff7bc8017 in pthread_join () from /lib64/libpthread.so.0
Missing separate debuginfos, use: debuginfo-install libgcc-4.8.5-44.el7.x86_64 libstdc++-4.8.5-44.el7.x86_64
(gdb) info threads 
  Id   Target Id                                 Frame 
* 1    Thread 0x7ffff7fe2740 (LWP 32301) "a.out" 0x00007ffff7bc8017 in pthread_join () from /lib64/libpthread.so.0
  2    Thread 0x7ffff6fd0700 (LWP 32305) "a.out" 0x00007ffff7bcd54d in __lll_lock_wait () from /lib64/libpthread.so.0

ठीक है, इस section में इतना ही, रुचि रखने वाले इस hardcore लेख को share करने के लिए स्वागत है! साथ ही, अपने thread pool project की सिफारिश करता हूँ, मैंने खुद video से इस project की व्याख्या की है, साथ में docker से one-click deployment भी है (इस episode का video, बाद में सबके लिए record करने की सोच रहा हूँ), रुचि हो तो मुझसे संपर्क करें।

संबंधित विषय

टिप्पणियाँ