2016-05-09 5 views
6

में एक लूप में लॉक करते समय थ्रेड भुखमरी मेरे पास एक ऐसा एप्लीकेशन है जो एक थ्रेड में लॉक में कुछ कार्य करता है। एक दूसरा थ्रेड भी है जो समय-समय पर से लॉक प्राप्त करना चाहता है। समस्या यह है कि, इस दूसरे थ्रेड को शायद ही कभी का मौका मिलता है, क्योंकि यह पहले काम करता है, क्योंकि पहले व्यक्ति हमेशा पहले लॉक करता है। मुझे आशा है कि निम्नलिखित कोड मैं क्या कहने की कोशिश कर रहा हूँ स्पष्ट होगा: आवेदन करने के लिए हो जाता हैपाइथन

import time 
from threading import Lock, Thread 

lock = Lock() 


def loop(): 
    while True: 
     with lock: 
      time.sleep(0.1) 

thread = Thread(target=loop) 
thread.start() 

before = time.time() 
lock.acquire() 
print('Took {}'.format(time.time() - before)) 

तो print तो आप देखेंगे कि यह जिस तरह से एक से अधिक सिर्फ 0.1s की जरूरत है। लेकिन कभी-कभी यह भी होता है कि यह अनिश्चित काल तक प्रतीक्षा करता है। मैंने इसे पाइथन 2.7.11 और पायथन 3.4.3 में डेबियन लिनक्स 8 पर परीक्षण किया है और यह समान रूप से काम करता है।

यह व्यवहार जवाबी सहज ज्ञान युक्त मेरे लिए है। loop में लॉक जारी होने पर, lock.acquire पहले ही इसकी रिलीज के लिए प्रतीक्षा कर रहा था और इसे तुरंत लॉक प्राप्त करना चाहिए। लेकिन इसके बजाय ऐसा लगता है कि लूप को लॉक पहले प्राप्त करने के लिए मिलता है, भले ही वह रिलीज़ पल पर रिलीज़ होने की प्रतीक्षा नहीं कर रहा था।

समाधान मैंने पाया खुला राज्य में प्रत्येक पाश यात्रा के बीच सोने के लिए है, लेकिन उस मुझे एक सुरुचिपूर्ण समाधान के रूप में हड़ताल नहीं करता, और क्या हो रहा है मुझे समझा है।

मुझे क्या याद आ रही है? CPython में

+0

यदि आप पारस्परिक बहिष्करण के बजाय निर्धारक व्यवहार चाहते हैं, तो 'लॉक' उपयोग करने का सही ऑब्जेक्ट नहीं है .. आप 'time.sleep (0)' जारी करके एक कार्य स्विच को मजबूर करने में सक्षम हो सकते हैं लेकिन मैं हूं यकीन है कि कुछ प्लेटफॉर्म पर असफल हो जाएगा .. – thebjorn

+0

ठीक है, मैंने यह पता लगाया है कि जब मेरे पास केवल दो धागे होते हैं तो जब कोई लॉक की प्रतीक्षा करता है तो यह हमेशा एक और अनलॉक के बाद ठीक हो जाएगा। मुझे आश्चर्य है कि ऐसा क्यों नहीं है। इसके अलावा, आपको क्या लगता है कि उपयोग करने के लिए एक उचित वस्तु होगी? –

+1

यह कहना मुश्किल है कि यह क्यों हो रहा है, लेकिन अगर मुझे लगता है कि कार्य स्विच के साथ ऐसा कुछ करना होगा जैसे कि लूप रीपस कोड उस बिंदु तक पहुंच गया है जहां यह प्रतीक्षा धागे के बीच चयन करता है। लेकिन यह सिर्फ अनुमान लगा रहा है। – thebjorn

उत्तर

4

ऐसा लगता है कि इस ओएस धागा शेड्यूलिंग के कारण है। मेरा अनुमान है कि या तो ओएस सीपीयू गहन धागे (जो भी इसका मतलब है) को या लॉक प्राप्त करने के लिए अगले धागे को बंद करने के लिए बहुत अधिक प्राथमिकता देता है (ओएस द्वारा किया गया) वास्तव में दूसरे धागे द्वारा लॉक प्राप्त करने से अधिक समय लेता है। किसी भी तरह से ओएस के आंतरिक जानने के बिना ज्यादा नहीं किया जा सकता है।

लेकिन यह इस कोड के बाद से जीआईएल नहीं है:

#include <mutex> 
#include <iostream> 
#include <chrono> 
#include <thread> 

std::mutex mutex; 

void my_thread() { 
    int counter = 100; 
    while (counter--) { 
     std::lock_guard<std::mutex> lg(mutex); 
     std::this_thread::sleep_for(std::chrono::milliseconds(500)); 
     std::cout << "." << std::flush; 
    } 
} 

int main (int argc, char *argv[]) { 
    std::thread t1(my_thread); 
    auto start = std::chrono::system_clock::now(); 
    // added sleep to ensure that the other thread locks lock first 
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
    { 
     std::lock_guard<std::mutex> lg(mutex); 
     auto end = std::chrono::system_clock::now(); 
     auto diff = end - start; 
     std::cout << "Took me " << diff.count() << std::endl; 
    } 
    t1.join(); 
    return 0; 
}; 

जो अपने कोड का सिर्फ एक सी ++ 11 संस्करण है बिल्कुल वैसा ही परिणाम (उबंटू 16.04 पर परीक्षण) देता है।

2

बहु सूत्रण थोड़ा जटिल है। कार्यान्वयन (अन्य चीजों के बीच स्मृति प्रबंधन के) को आसान बनाने के लिए, सीपीथन में एक अंतर्निहित "ग्लोबल इंटरप्रेटर लॉक" है। यह ताला सुनिश्चित करता है कि एक समय में केवल एक थ्रेड पाइथन बाइटकोड निष्पादित किया जा सकता है।

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

अधिक गहराई से देखने के लिए, डेविड बीज़ली द्वारा वीडियो understanding the Python GIL वीडियो देखें।

+0

यह जीआईएल नहीं है (विचार शुरुआत से मुझे गंध था), मेरा जवाब देखें। – freakish

+0

@freakish फिर से पढ़ें। –

+1

मैंने इसे कई बार पढ़ा है। जीआईएल समस्या से पूरी तरह से असंबंधित है। इसके बारे में बात करना भ्रमित है और बिल्कुल मदद नहीं करता है। आपका पूरा उत्तर 1 वाक्य तक कम किया जाना चाहिए: 'ओएस में शेड्यूलिंग में भी एक कहना है'। – freakish