2012-08-04 22 views
6

मुझे एक सिंक्रोनस सी प्रोग्राम को संशोधित करने का कार्य सौंपा गया है ताकि यह समानांतर में चल सके। लक्ष्य यह संभव है कि यह पोर्टेबल जितना संभव हो क्योंकि यह एक ओपन सोर्स प्रोग्राम है जिसे कई लोग उपयोग करते हैं। इस वजह से, मैंने सोचा कि प्रोग्राम को सी ++ परत में लपेटना सबसे अच्छा होगा ताकि मैं पोर्टेबल बूस्ट लाइब्रेरी का लाभ उठा सकूं। मैंने पहले ही यह किया है और सबकुछ अपेक्षित काम करता है।मल्टी-थ्रेडेड सी ++ संदेश पासिंग

मेरी समस्या यह है कि धागे के बीच संदेशों को पारित करने का सबसे अच्छा तरीका क्या है, यह तय कर रहा है। सौभाग्य से, कार्यक्रम की वास्तुकला एक बहु निर्माता और एकल उपभोक्ता है। इससे भी बेहतर, संदेशों का क्रम महत्वपूर्ण नहीं है। मैंने पढ़ा है कि एकल-निर्माता/सिंगल-उपभोक्ता (एसपीएससी) कतार इस वास्तुकला से लाभान्वित होंगे। बहु थ्रेडेड प्रोग्रामिंग के साथ अनुभवी लोगों को कोई सलाह है? मैं इस सामान के लिए काफी नया हूँ। एसपीएससी को लागू करने के लिए बढ़ावा देने के साथ-साथ किसी भी कोड उदाहरण की सराहना की जाएगी।

+0

http://stackoverflow.com/questions/8918401/does-a-multiple-producer-single-consumer-lock-free-queue-exist-for-c – walrii

उत्तर

7

नीचे वह तकनीक है जिसका उपयोग मैंने अपने सहकारी मल्टी-टास्किंग/मल्टी-थ्रेडिंग लाइब्रेरी (मैसे) http://bytemaster.github.com/mace/ के लिए किया था। कतार खाली होने के अलावा इसे लॉक-फ्री होने का लाभ है।

struct task { 
    boost::function<void()> func; 
    task* next; 
}; 


boost::mutex      task_ready_mutex; 
boost::condition_variable  task_ready; 
boost::atomic<task*>    task_in_queue; 

// this can be called from any thread 
void thread::post_task(task* t) { 
    // atomically post the task to the queue. 
    task* stale_head = task_in_queue.load(boost::memory_order_relaxed); 
    do { t->next = stale_head; 
    } while(!task_in_queue.compare_exchange_weak(stale_head, t, boost::memory_order_release)); 

    // Because only one thread can post the 'first task', only that thread will attempt 
    // to aquire the lock and therefore there should be no contention on this lock except 
    // when *this thread is about to block on a wait condition. 
    if(!stale_head) { 
     boost::unique_lock<boost::mutex> lock(task_ready_mutex); 
     task_ready.notify_one(); 
    } 
} 

// this is the consumer thread. 
void process_tasks() { 
    while(!done) { 
    // this will atomically pop everything that has been posted so far. 
    pending = task_in_queue.exchange(0,boost::memory_order_consume); 
    // pending is a linked list in 'reverse post order', so process them 
    // from tail to head if you want to maintain order. 

    if(!pending) { // lock scope 
     boost::unique_lock<boost::mutex> lock(task_ready_mutex);     
     // check one last time while holding the lock before blocking. 
     if(!task_in_queue) task_ready.wait(lock); 
    } 
} 
+0

'सहकारी': (( –

+0

.. हालांकि कतार के भीतर संदेश संग्रहण से बचने के लिए प्रत्येक संदेश के अंदर एक लिंक का उपयोग करने के लिए +1। –

+0

शानदार धन्यवाद। – grouma

1

यदि केवल एक ही उपभोक्ता है लेकिन एकाधिक उत्पादक हैं, तो मैं ओ (1) एक्सेस समय के साथ एक सरणी या कुछ सरणी जैसी डेटा-संरचना का उपयोग करूंगा जहां प्रत्येक सरणी-स्लॉट एकल-निर्माता-उपभोक्ता कतार का प्रतिनिधित्व करता है। एकल-निर्माता-उपभोक्ता कतार का बड़ा लाभ यह तथ्य है कि आप इसे किसी भी स्पष्ट सिंक्रनाइज़ेशन तंत्र के बिना लॉक-फ्री बना सकते हैं, इस प्रकार इसे बहु-थ्रेडेड वातावरण में बहुत तेज़ डेटा-स्ट्रक्चर बनाते हैं। एकल निर्माता-उपभोक्ता कतार के नंगे हड्डियों के कार्यान्वयन के लिए my answer here देखें।

+0

पर स्वीकृत उत्तर देखें मैंने इस तकनीक का उपयोग करने से पहले इस तकनीक का उपयोग किया नीचे परमाणु समाधान। मेरी समस्या यह थी कि यह स्केल नहीं किया गया था, बफर के लिए 'निष्क्रिय' होने पर यह स्मृति का उपभोग करता था, और यदि आपको नहीं पता कि कौन से थ्रेड अग्रिम (सामान्य) में पोस्ट कर रहे हैं, तो आपको या तो गतिशील रूप से आकार बदलना होगा (एक के माध्यम से लॉक) या एक 'बड़ी' अधिकतम धागा गिनती हार्डकोड। – bytemaster

+0

मैं एक गोलाकार कतार का उपयोग करने के बारे में सोच रहा था ... इस तरह कतार को फिर से आकार देने की कोई आवश्यकता नहीं है। – Jason

+0

जो 'निश्चित आकार कतार' है जो भर सकता है, लेकिन यदि आपके पास एन धागे हैं तो प्रत्येक थ्रेड को अन्य सभी एन धागे के लिए एक इनपुट की आवश्यकता होती है, या पहली बार जब कोई नया धागा दूसरे के साथ संवाद करने का प्रयास करता है तो उसे अपना आवंटन करना होगा एकल निर्माता/एकल उपभोक्ता कतार। यह आकार कोड बनाम हार्ड कोड मुद्दा है। – bytemaster

1

नेट पर निर्माता-उपभोक्त कतारों के कई उदाहरण, कई उत्पादकों/उपभोक्ताओं के लिए सुरक्षित कर रहे हैं। @bytemaster ने पोस्ट किया है जो कतार वर्ग में भंडारण को खत्म करने के लिए प्रत्येक संदेश के अंदर एक लिंक का उपयोग करता है - यह एक अच्छा दृष्टिकोण है, मैं इसे स्वयं एम्बेडेड नौकरियों पर उपयोग करता हूं।

जहां कतार वर्ग को भंडारण प्रदान करना होगा, मैं आमतौर पर आकार एन की 'पूल कतार' के साथ जाता हूं, स्टार्टअप पर एन * संदेश वर्ग के उदाहरणों के साथ लोड होता है। जिन धागे को संवाद करने की आवश्यकता है उन्हें पूल से * संदेश पॉप करना है, इसे लोड करना और इसे कतार करना है। आखिरकार 'इस्तेमाल किया' * संदेश पूल पर वापस धकेल दिया जाता है। यह संदेशों की संख्या को कैप्स करता है और इसलिए सभी कतारों की लंबाई केवल लंबाई की आवश्यकता होती है - कोई आकार नहीं, कोई नया(), कोई डिलीट नहीं(), आसान रिसाव-पहचान।

+0

मैंने जवाब के लिए कोड को सरल बना दिया, लेकिन हकीकत में हर 'कार्य' 'अज्ञात आकार' के एक मज़ेदार को संग्रहीत कर रहा है (बढ़ावा :: फ़ंक्शन <> के ढेर आवंटन से बचने के लिए)। इसके अलावा, मैंने एक संदर्भ के रूप में कार्य को दोगुना करने दिया है, जब तक कि भविष्य में इसे जीवित रखा जाता है। मैं प्रति कार्य 1 मॉलोक के साथ समाप्त होता हूं और प्रति सेकंड 2.4 मिलियन सिंक पोस्ट/प्रतीक्षा ऑप्स को धक्का दे सकता हूं। – bytemaster