2008-10-03 9 views
8

मान लें कि मेरे पास एक बहुप्रचारित सी ++ प्रोग्राम है जो फ़ंक्शन कॉल के रूप में handleRequest(string key) पर अनुरोधों को संभालता है। handleRequest पर प्रत्येक कॉल एक अलग थ्रेड में होता है, और key के लिए मनमाने ढंग से संभावित मानों की बड़ी संख्या होती है।मैं सी ++ में लॉक के रूप में एक मनमानी स्ट्रिंग का उपयोग कैसे करूं?

मैं निम्नलिखित व्यवहार चाहते हैं: वे key के लिए एक ही मूल्य है जब

  • handleRequest(key) को एक साथ कॉल क्रमांकित हैं।
  • वैश्विक क्रमबद्धता कम हो गई है।

handleRequest के शरीर इस प्रकार दिखाई देंगे:

void handleRequest(string key) { 
    KeyLock lock(key); 
    // Handle the request. 
} 

प्रश्न: मैं KeyLock कैसे लागू होगा आवश्यक व्यवहार पाने के लिए?

एक अनुभवहीन कार्यान्वयन इस तरह से शुरू हो सकता है:

KeyLock::KeyLock(string key) { 
    global_lock->Lock(); 
    internal_lock_ = global_key_map[key]; 
    if (internal_lock_ == NULL) { 
     internal_lock_ = new Lock(); 
     global_key_map[key] = internal_lock_; 
    } 
    global_lock->Unlock(); 
    internal_lock_->Lock(); 
} 

KeyLock::~KeyLock() { 
    internal_lock_->Unlock(); 
    // Remove internal_lock_ from global_key_map iff no other threads are waiting for it. 
} 

... लेकिन यह है कि शुरुआत में एक वैश्विक ताला और प्रत्येक अनुरोध के अंत में, और प्रत्येक अनुरोध के लिए एक अलग Lock वस्तु के निर्माण की आवश्यकता है। यदि handleRequest पर कॉल के बीच विवाद अधिक है, तो यह कोई समस्या नहीं हो सकती है, लेकिन अगर विवाद कम हो तो यह बहुत अधिक ओवरहेड लगा सकता है।

उत्तर

12

आप अपने प्रश्न में जो कुछ भी रखते हैं उसके समान कुछ कर सकते हैं, लेकिन एक वैश्विक_की_मैप के बजाय कई (शायद एक सरणी या वेक्टर में) - जिसका उपयोग किया जाता है स्ट्रिंग पर कुछ सरल हैश फ़ंक्शन द्वारा निर्धारित किया जाता है।

इस तरह एक वैश्विक लॉक के बजाय, आप इसे कई स्वतंत्र लोगों में फैलाते हैं।

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

2

यह मंच पर निर्भर करेगा, लेकिन दो तकनीकों है कि मैं कोशिश करेंगे:

  • उपयोग म्युटेक्स/तुल्यकालन वस्तुओं, जहां किसी चीज़ का नाम = कुंजी फाइल सिस्टम आधारित लॉकिंग
  • उपयोग नामित , जहां आप कुंजी नाम के साथ गैर-साझा करने योग्य अस्थायी फ़ाइल बनाने का प्रयास करें। यह पहले से ही मौजूद है, तो (= पहले से ही बंद कर दिया ) इस असफल हो जायेगी और आप

दोनों तकनीकों पुन: प्रयास करने के लिए अपने ओएस के विस्तार पर निर्भर करेगा मतदान करना होगा। प्रयोग करें और देखें कि कौन सा काम करता है। ।

+0

आम तौर पर केवल कई नामित म्यूटेक्स बना सकते हैं। लिनक्स पर कम से कम आप कितने प्राप्त कर सकते हैं, लेकिन मैं इस विधि का उपयोग पुराने म्यूटेक्स एकत्र करने के लिए कुछ कचरे के साथ करने से सावधान रहूंगा। –

2

शायद std::map<std::string, MutexType> आप जो चाहते हैं वह होगा, जहां MutexType आपके इच्छित म्यूटेक्स का प्रकार है।आपको यह सुनिश्चित करने के लिए शायद किसी अन्य म्यूटेक्स में मानचित्र पर पहुंच को लपेटना पड़ेगा ताकि एक ही थ्रेड एक ही समय में डालने वाला हो (और म्यूटेक्स लॉक होने के बाद फिर से जांच करने के लिए याद रखें कि एक और धागा ने यह नहीं जोड़ा है म्यूटेक्स पर इंतजार करते समय कुंजी!)।

एक ही सिद्धांत किसी भी अन्य सिंक्रनाइज़ेशन विधि, जैसे एक महत्वपूर्ण खंड पर लागू हो सकता है।

+1

यह वही कार्यान्वयन है क्योंकि ओपी का कहना है कि वह नहीं चाहता है। –

2

बढ़ाएं स्पष्टता और पूरे लॉक कुंजी-पर्वतमाला

यह माइक बी का जवाब है, जहां के बजाय कई तरल पदार्थ ताला होने के नक्शे आप ताले की एक एकल तय सरणी कि मुख्य श्रेणियों के लिए बजाय लागू है पर एक भिन्नता है एकल चाबियाँ

सरलीकृत उदाहरण: स्टार्टअप पर 256 लॉक की सरणी बनाएं, फिर लॉक होने के सूचकांक को निर्धारित करने के लिए कुंजी के पहले बाइट का उपयोग करें (यानी 'के' से शुरू होने वाली सभी कुंजी locks[107] द्वारा संरक्षित की जाएंगी)।

इष्टतम थ्रूपुट को बनाए रखने के लिए आपको कुंजी और विवाद दर का वितरण करना चाहिए। इस दृष्टिकोण के लाभ शून्य गतिशील आवंटन और सरल सफाई हैं; आप दो-चरण लॉकिंग से भी बचते हैं। यदि समय के साथ मुख्य वितरण कम हो जाता है तो नकारात्मक पक्ष संभावित विचलन शिखर होता है।

0

इस बारे में सोच करने के बाद, एक और दृष्टिकोण कुछ इस तरह जाना हो सकता है:

  • handleRequest में, एक Callback है कि वास्तविक काम करता है बनाएँ।
  • एक म्यूटेक्स द्वारा संरक्षित multimap<string, Callback*> global_key_map बनाएं।
  • यदि कोई धागा देखता है कि key पहले ही संसाधित हो रहा है, तो यह Callback* को global_key_map में जोड़ता है और रिटर्न देता है।
  • अन्यथा, यह तुरंत कॉलबैक कॉल करता है, और उसके बाद उसी कुंजी के लिए दिखाए गए कॉलबैक को कॉल करता है।

इस तरह कार्यान्वित कुछ:

LockAndCall(string key, Callback* callback) { 
    global_lock.Lock(); 
    if (global_key_map.contains(key)) { 
     iterator iter = global_key_map.insert(key, callback); 
     while (true) { 
      global_lock.Unlock(); 
      iter->second->Call(); 
      global_lock.Lock(); 
      global_key_map.erase(iter); 
      iter = global_key_map.find(key); 
      if (iter == global_key_map.end()) { 
       global_lock.Unlock(); 
       return; 
      } 
     } 
    } else { 
     global_key_map.insert(key, callback); 
     global_lock.Unlock(); 
    } 
} 

इस धागे कि अन्यथा एक कुंजी लॉक इंतजार करेगी को मुक्त का लाभ दिया है, लेकिन अलग से यह काफी अनुभवहीन समाधान के रूप में एक ही है कि मैं प्रश्न में तैनात

हालांकि इसे माइक बी और कॉन्स्टेंटिन द्वारा दिए गए उत्तरों के साथ जोड़ा जा सकता है।

0
 /** 
     * StringLock class for string based locking mechanism 
     * e.g. usage 
     *  StringLock strLock; 
     *  strLock.Lock("row1"); 
     *  strLock.UnLock("row1"); 
     */ 
     class StringLock { 
     public: 
      /** 
      * Constructor 
      * Initializes the mutexes 
      */ 
      StringLock() { 
       pthread_mutex_init(&mtxGlobal, NULL); 
      } 
      /** 
      * Lock Function 
      * The thread will return immediately if the string is not locked 
      * The thread will wait if the string is locked until it gets a turn 
      * @param string the string to lock 
      */ 
      void Lock(string lockString) { 
       pthread_mutex_lock(&mtxGlobal); 
       TListIds *listId = NULL; 
       TWaiter *wtr = new TWaiter; 
       wtr->evPtr = NULL; 
       wtr->threadId = pthread_self(); 
       if (lockMap.find(lockString) == lockMap.end()) { 
        listId = new TListIds(); 
        listId->insert(listId->end(), wtr); 
        lockMap[lockString] = listId; 
        pthread_mutex_unlock(&mtxGlobal); 
       } else { 
        wtr->evPtr = new Event(false); 
        listId = lockMap[lockString]; 
        listId->insert(listId->end(), wtr); 
        pthread_mutex_unlock(&mtxGlobal); 
        wtr->evPtr->Wait(); 
       } 
      } 
      /** 
      * UnLock Function 
      * @param string the string to unlock 
      */ 
      void UnLock(string lockString) { 
       pthread_mutex_lock(&mtxGlobal); 
       TListIds *listID = NULL; 
       if (lockMap.find(lockString) != lockMap.end()) { 
        lockMap[lockString]->pop_front(); 
        listID = lockMap[lockString]; 
        if (!(listID->empty())) { 
         TWaiter *wtr = listID->front(); 
         Event *thdEvent = wtr->evPtr; 
         thdEvent->Signal(); 
        } else { 
         lockMap.erase(lockString); 
         delete listID; 
        } 
       } 
       pthread_mutex_unlock(&mtxGlobal); 
      } 
     protected: 
      struct TWaiter { 
       Event *evPtr; 
       long threadId; 
      }; 
      StringLock(StringLock &); 
      void operator=(StringLock&); 
      typedef list TListIds; 
      typedef map TMapLockHolders; 
      typedef map TMapLockWaiters; 
     private: 
      pthread_mutex_t mtxGlobal; 
      TMapLockWaiters lockMap; 
     }; 
+0

यह वही "ग्लोबल लॉक" कार्यान्वयन है जिसे ओपी कहते हैं कि वह नहीं चाहता है। इसके अलावा, एक स्टाइलिस्टिक और कोड-पुन: प्रयोज्यता नोट पर: आप दोनों मानक पर्थ्रेड * और * यह अपरिभाषित 'ईवेंट' चीज (जो मूल रूप से एक pthreads 'sem_t' की तरह दिखता है) का उपयोग कर रहे हैं। – Quuxplusone

संबंधित मुद्दे

 संबंधित मुद्दे