2011-06-17 18 views
5

मान लें कि मैं एक बंदरगाह पर प्राप्त करने के लिए धागा शुरू करता हूं। सॉकेट कॉल रिकॉर्फ़ पर अवरुद्ध होगा। फिर, किसी अन्य धागे में, मैं सॉकेट बंद करता हूं।सॉकेट बंद होने पर रिकवर से अनब्लॉक करें

विंडोज़ पर, यह रिकवर से अनब्लॉक करेगा और मेरा थ्रेड निष्पादन समाप्त हो जाएगा।

लिनक्स पर, यह रिकवर से अनब्लॉक नहीं करता है, और नतीजतन, मेरा धागा हमेशा के लिए कुछ भी नहीं कर रहा है, और थ्रेड निष्पादन समाप्त नहीं होता है।

क्या कोई मेरी मदद कर सकता है लिनक्स पर क्या हो रहा है? जब सॉकेट बंद हो जाता है, तो मैं

अनवरोधित करने के लिए रिकॉर्फ़ॉर्म करना चाहता हूं, मैं चुनिंदा() का उपयोग करने के बारे में पढ़ता रहता हूं, लेकिन मुझे नहीं पता कि मेरे विशिष्ट मामले के लिए इसका उपयोग कैसे किया जाए।

उत्तर

1
नहीं

एक जवाब है, लेकिन लिनक्स पास आदमी पेज दिलचस्प उद्धरण शामिल हैं:

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

+0

यही कारण है कि है: यहाँ कैसे मैं एक विचार "मतदान" कहा जाता है का उपयोग कर समस्या हल का एक संक्षिप्त सारांश है (कार्यक्रम के रूप में अच्छी तरह से चलाने की कोशिश करें, प्रिंट बयान आप के चल रहा क्या एक ठोस विचार दे देंगे) मैंने कहा "किसी भी तरह से एक और धागे में, मैं सॉकेट बंद करता हूं ... इस मामले को संभालने की ज़रूरत है और मैं नहीं चाहता कि मेरे कार्यक्रम की संभावना हमेशा के लिए लटकती हुई हो क्योंकि एक बंदरगाह पर अवरुद्ध धागे की वजह से – user657178

+0

@ user657178 इस मामले को संभालने की आवश्यकता नहीं है क्योंकि कोड जो अन्यथा सही है, संभवतः * इस मामले को ट्रिगर नहीं कर सकता है। –

3

यहाँ एक आसान तरीका का चित्र का उपयोग करने के चयन() इस समस्या से निपटने के लिए:

// Note: untested code, may contain typos or bugs 
static volatile bool _threadGoAway = false; 

void MyThread(void *) 
{ 
    int fd = (your socket fd); 
    while(1) 
    { 
     struct timeval timeout = {1, 0}; // make select() return once per second 

     fd_set readSet; 
     FD_ZERO(&readSet); 
     FD_SET(fd, &readSet); 

     if (select(fd+1, &readSet, NULL, NULL, &timeout) >= 0) 
     { 
     if (_threadGoAway) 
     { 
      printf("MyThread: main thread wants me to scram, bye bye!\n"); 
      return; 
     } 
     else if (FD_ISSET(fd, &readSet)) 
     { 
      char buf[1024]; 
      int numBytes = recvfrom(fd, buf, sizeof(buf), 0); 
      [...handle the received bytes here...] 
     } 
     } 
     else perror("select"); 
    } 
} 

// To be called by the main thread at shutdown time 
void MakeTheReadThreadGoAway() 
{ 
    _threadGoAway = true; 
    (void) pthread_join(_thread, NULL); // may block for up to one second 
} 

एक और अधिक सुरुचिपूर्ण विधि का चयन के समय समाप्ति सुविधा का उपयोग कर से बचने, और बदले बनाने के लिए किया जाएगा एक सॉकेट जोड़ी (सॉकेटपेयर() का उपयोग करके) और मुख्य धागा सॉकेट जोड़ी के अंत में एक बाइट भेजता है जब यह I/O थ्रेड दूर जाना चाहता है, और जब I/O थ्रेड बाहर निकलता है तो उसे बाइट प्राप्त होता है सॉकेटपेयर के दूसरे छोर पर सॉकेट। हालांकि मैं इसे पाठक के लिए एक अभ्यास के रूप में छोड़ दूंगा। :)

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

+0

और मैं आपको फिर से याद दिलाता हूं कि उस प्रश्न के लिए "स्वीकृत" उत्तर बस गलत है। प्रत्येक यूनिक्स - मूल बीएसडी से और एसवाईएस वी ऑनवर्ड - ने गारंटी दी है कि पढ़ने के बाद() _never_ ब्लॉक होगा() कहते हैं कि सॉकेट तैयार है। और पॉज़िक्स spec भी करता है। यदि लिनक्स अलग-अलग व्यवहार करता है, तो यह लिनक्स में एक बग है। टूटे हुए सिस्टम को पूरा करने के लिए आपको लोगों को कचरे के साथ अपने कोड को प्रदूषित करने के लिए प्रोत्साहित नहीं करना चाहिए। – Nemo

+0

हाय निमो, अगर आप कह रहे हैं कि लिनक्स में बग अब तय है और इसलिए मेरी चेतावनी भ्रामक है, यह एक बात है। यदि ओटीओएच आप कह रहे हैं कि बग अभी भी मौजूद है, लेकिन किसी को भी इसके बारे में कभी बात नहीं करनी चाहिए, तो आप केवल लोगों को असफल होने के लिए सेट कर रहे हैं। लिनक्स के (गलत) व्यवहार से अनजान लोगों को रखने से समस्या उन्हें रोकने से रोका नहीं जायेगी, इससे उन्हें केवल इस मुद्दे से निपटने के लिए अग्रिम योजना बनाने में मदद मिलेगी। –

+0

एक उचित बिंदु। मुझे लगता है कि इस बग को एक गैर-शून्य मौका दिया गया है, क्योंकि किसी भी अच्छे कारण के लिए पॉज़िक्स का उल्लंघन करने के लिए इतनी स्पष्ट नहीं है कि लिनक्स डेवलपर्स बेवकूफ नहीं हैं। मैं लिनक्स-कर्नेल मेलिंग सूची पर पूछने की कोशिश करूंगा। और माफी माँगता हूं कि मेरा स्वर अत्यधिक शत्रुतापूर्ण था; मेरे पास एक बुरा दिन था ... – Nemo

11

सॉकेट पर shutdown(sock, SHUT_RDWR) पर कॉल करें, फिर थ्रेड को बाहर निकलने के लिए प्रतीक्षा करें। (यानी pthread_join)।

आपको लगता है कि close()recvfrom() को अनवरोधित करेगा, लेकिन यह लिनक्स पर नहीं है।

+0

SHUT_RD पर्याप्त है। – EJP

+0

यह किसी भी * सिस्टम पर 'recvfrom' को अनब्लॉक करने की गारंटी नहीं है, और संभवतः नहीं हो सकता है। एक अंतर्निहित दौड़ है जिसे केवल उपयोगकर्ता स्थान में सिंक्रनाइज़ेशन द्वारा हल किया जा सकता है। –

0

आप असंभव के लिए पूछ रहे हैं। close पर कॉल करने के लिए recvfrom में अन्य थ्रेड को अवरुद्ध करने वाले थ्रेड के लिए बस कोई संभावित तरीका नहीं है। कोड लिखने का प्रयास करें जो गारंटी देता है कि ऐसा होता है, आप पाएंगे कि यह असंभव है।

कोई फर्क नहीं पड़ता कि आप क्या करते हैं, close पर कॉल के लिए recvfrom पर कॉल के लिए दौड़ के लिए हमेशा संभव होगा। close पर कॉल सॉकेट डिस्क्रिप्टर को संदर्भित करता है, इसलिए यह कॉल के अर्थपूर्ण अर्थ को recvfrom पर बदल सकता है।

recvfrom में थ्रेड के लिए कोई रास्ता नहीं है जो किसी भी तरह से close पर कॉल करने वाले थ्रेड को सिग्नल करने के लिए प्रवेश करता है (अवरुद्ध होने या सिस्टम कॉल में प्रवेश करने के विरोध में)।इसलिए close और recvfrom के व्यवहार को सुनिश्चित करने के लिए सचमुच कोई संभावित तरीका नहीं है।

निम्नलिखित पर विचार करें:

  1. एक धागा बारे में recvfrom कॉल करने के लिए है, लेकिन यह हो जाता है अन्य बातों प्रणाली करने की जरूरत से पूर्व empted।
  2. बाद में, थ्रेड close पर कॉल करता है।
  3. सिस्टम की I/O लाइब्रेरी द्वारा शुरू किया गया एक थ्रेड socket पर कॉल करता है और उसी डिक्रिप्टर को आपके close डी के रूप में प्राप्त करता है।
  4. अंत में, थ्रेड recvfrom पर कॉल करता है, और अब यह लाइब्रेरी खोले सॉकेट से प्राप्त हो रहा है।

ओह।

कभी भी इस तरह से दूरस्थ रूप से कुछ भी नहीं किया। एक संसाधन जारी नहीं किया जाना चाहिए जबकि एक और धागा इसका उपयोग कर रहा है, या हो सकता है। अवधि।

0

जब सॉकेट बंद कर दिया है, मैं अनब्लॉक करने के लिए recvfrom

recvfrom() एक समारोह अजगर पर यूडीपी सॉकेट के लिए विशिष्ट है चाहता हूँ।

import socket 
import threading 
import signal 
import time 

# Custom class to create a socket, close the socket, and poll the socket 
class ServerSocket(): 
    def __init__(self, addresses): 
     # "Standard" way to create and preapare a working socket 
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
     self.socket.bind(addresses) 
    def poll(self): 
     self.socket.settimeout(2) 
     modifiedMsg, senderAddress = self.socket.recvfrom(1024) 
    def close(self): 
     self.socket.close() 

class ServiceExit(Exception): 
    """ 
    Custom exception which is used to trigger the clean exit 
    of all running threads and the main program. 
    """ 
    pass 

def service_shutdown(signum, frame): 
    raise ServiceExit 

# Custom class to create a UDP server on a separate thread. 
# This server will know to close the blocking UDP socket when the user 
# of the main program signals termination via typing CTRL-C into the terminal 
class Server(threading.Thread): 
    def __init__(self, addresses): 
     threading.Thread.__init__(self) 
     self.mysocket = ServerSocket(addresses) 
     # This flag below will help us determine when to stop the "run" loop below 
     # The while loop below is where interrupt the blocking recvfrom() call by 
     # timing out every 2 seconds and checking to see if the flag has been set 
     # to discontinue the while loop 
     self.shutdown_flag = threading.Event() 
    def run(self): 
     while not self.shutdown_flag.is_set(): 
      try: 
       print('socket blocking') 
       self.mysocket.poll() 
      except socket.timeout: 
       print('socket unblocked') 
       pass 
     # as a final step, we close the socket 
     self.mysocket.close() 
     print('socket closed') 

def main(): 
    # assign the methods that will be called when our main program receives a SIGTERM or SIGINT signal 
    # You can send this main problem such a signal by typing CTRL-C after you run this program 
    signal.signal(signal.SIGTERM, service_shutdown) 
    signal.signal(signal.SIGINT, service_shutdown) 

    # Start the server thread that will eventually block on recvfrom() 
    try: 
     print('starting udp server thread') 
     udp_server = Server(('localhost', 5000)) 
     udp_server.start() 
     while True: 
      time.sleep(0.5) 
     # This server will accept UDP packets on the local host at port 5000 
     # Feel free to change these settings to fit your needs 
    except ServiceExit: 
     print('shutting down server thread') 
     udp_server.shutdown_flag.set() 
     udp_server.join() 
     print('server thread shut down') 

if __name__ == '__main__': 
    main()