क्या किसी थ्रेड को मारने का कोई तरीका है?


764

क्या किसी झंडे / सेमाफोरस / आदि की स्थापना / जाँच के बिना किसी चल रहे धागे को समाप्त करना संभव है?

जवाबों:


672

यह आमतौर पर पायथन और किसी भी भाषा में अचानक एक धागे को मारने के लिए एक बुरा पैटर्न है। निम्नलिखित मामलों के बारे में सोचो:

  • धागा एक महत्वपूर्ण संसाधन पकड़ रहा है जिसे ठीक से बंद किया जाना चाहिए
  • धागे ने कई अन्य धागे बनाए हैं जिन्हें अवश्य ही मारना चाहिए।

इसे संभालने का अच्छा तरीका है अगर आप इसे बर्दाश्त कर सकते हैं (यदि आप अपने स्वयं के धागे का प्रबंधन कर रहे हैं) के पास एक एक्जिट_रेक्वेस्ट झंडा है जिसे प्रत्येक थ्रेड नियमित अंतराल पर जांचता है कि क्या यह बाहर निकलने का समय है।

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

import threading

class StoppableThread(threading.Thread):
    """Thread class with a stop() method. The thread itself has to check
    regularly for the stopped() condition."""

    def __init__(self,  *args, **kwargs):
        super(StoppableThread, self).__init__(*args, **kwargs)
        self._stop_event = threading.Event()

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()

इस कोड में, आपको stop()थ्रेड पर कॉल करना चाहिए जब आप इसे बाहर निकलना चाहते हैं, और थ्रेड के ठीक से उपयोग करने के लिए प्रतीक्षा करें join()। धागे को नियमित अंतराल पर स्टॉप फ्लैग की जांच करनी चाहिए।

हालांकि ऐसे मामले हैं जब आपको वास्तव में एक धागे को मारने की आवश्यकता होती है। एक उदाहरण है जब आप एक बाहरी पुस्तकालय लपेट रहे हैं जो लंबी कॉल के लिए व्यस्त है और आप इसे बाधित करना चाहते हैं।

निम्नलिखित कोड पायथन धागे में एक अपवाद को बढ़ाने के लिए (कुछ प्रतिबंधों के साथ) अनुमति देता है:

def _async_raise(tid, exctype):
    '''Raises an exception in the threads with id tid'''
    if not inspect.isclass(exctype):
        raise TypeError("Only types can be raised (not instances)")
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
                                                     ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # "if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"
        ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

class ThreadWithExc(threading.Thread):
    '''A thread class that supports raising exception in the thread from
       another thread.
    '''
    def _get_my_tid(self):
        """determines this (self's) thread id

        CAREFUL : this function is executed in the context of the caller
        thread, to get the identity of the thread represented by this
        instance.
        """
        if not self.isAlive():
            raise threading.ThreadError("the thread is not active")

        # do we have it cached?
        if hasattr(self, "_thread_id"):
            return self._thread_id

        # no, look for it in the _active dict
        for tid, tobj in threading._active.items():
            if tobj is self:
                self._thread_id = tid
                return tid

        # TODO: in python 2.6, there's a simpler way to do : self.ident

        raise AssertionError("could not determine the thread's id")

    def raiseExc(self, exctype):
        """Raises the given exception type in the context of this thread.

        If the thread is busy in a system call (time.sleep(),
        socket.accept(), ...), the exception is simply ignored.

        If you are sure that your exception should terminate the thread,
        one way to ensure that it works is:

            t = ThreadWithExc( ... )
            ...
            t.raiseExc( SomeException )
            while t.isAlive():
                time.sleep( 0.1 )
                t.raiseExc( SomeException )

        If the exception is to be caught by the thread, you need a way to
        check that your thread has caught it.

        CAREFUL : this function is executed in the context of the
        caller thread, to raise an excpetion in the context of the
        thread represented by this instance.
        """
        _async_raise( self._get_my_tid(), exctype )

( टोमर फिलिबा द्वारा किलरेबल थ्रेड्स के आधार पर । वापसी मूल्य के बारे में उद्धरण पायथन केPyThreadState_SetAsyncExc एक पुराने संस्करण से प्रतीत होता है ।)

जैसा कि प्रलेखन में उल्लेख किया गया है, यह एक जादू की गोली नहीं है क्योंकि यदि धागा पायथन दुभाषिया के बाहर व्यस्त है, तो यह रुकावट को नहीं पकड़ेगा।

इस कोड का एक अच्छा उपयोग पैटर्न धागा को एक विशिष्ट अपवाद को पकड़ना और सफाई करना है। इस तरह, आप एक कार्य को बाधित कर सकते हैं और अभी भी उचित सफाई कर सकते हैं।


78
@ Bluebird75: इसके अलावा, मुझे यकीन नहीं है कि मुझे यह तर्क मिलता है कि धागे को अचानक नहीं मारा जाना चाहिए "क्योंकि धागा एक महत्वपूर्ण संसाधन को पकड़ सकता है जिसे ठीक से बंद किया जाना चाहिए": यह एक मुख्य कार्यक्रम, और मुख्य कार्यक्रमों से भी सच है उपयोगकर्ता द्वारा अचानक मारे जा सकते हैं (उदाहरण के लिए, यूनिक्स में Ctrl-C) -जिसके कारण वे इस संभावना को यथासंभव अच्छी तरह से संभालने की कोशिश करते हैं। इसलिए, मैं यह देखने में विफल हूं कि थ्रेड्स के साथ क्या विशेष है, और उन्हें मुख्य कार्यक्रमों के समान उपचार क्यों नहीं प्राप्त करना चाहिए (जैसे कि उन्हें अचानक से मार दिया जा सकता है)। :) क्या आपक लिए इसे विस्तार से कहना संभव है?
एरिक ओ लेबिगॉट

18
@ ईओएल: दूसरी ओर, यदि सभी संसाधन जो थ्रेड के मालिक हैं, वे स्थानीय संसाधन (खुली फाइलें, सॉकेट) हैं, तो लिनक्स प्रक्रिया की सफाई में उचित रूप से अच्छा है और यह रिसाव नहीं करता है। हालांकि मेरे पास ऐसे मामले थे जहां मैंने सॉकेट का उपयोग करके एक सर्वर बनाया था, और अगर मैं Ctrl-C के साथ एक क्रूर व्यवधान करता हूं, तो मैं प्रोग्राम को गैर-लॉन्च कर सकता हूं क्योंकि यह सॉकेट को बांध नहीं सकता है। मुझे 5 मिनट इंतजार करने की जरूरत है। उचित समाधान Ctrl-C को पकड़ना था और सॉकेट डिक्नेक्शन को साफ करना था।
फिलिप एफ

10
@ Bluebird75: btw। आप त्रुटि SO_REUSEADDRसे बचने के लिए सॉकेट विकल्प का उपयोग कर सकते हैं Address already in use
मेसा

12
इस उत्तर के बारे नोट: मुझे (py2.6) के लिए कम से कम, मैं पारित करने के लिए किया था Noneके बजाय 0के लिए res != 1मामला है, और मैं फोन करने के लिए किया था ctypes.c_long(tid)और पारित करने के लिए कि किसी भी ctypes टीआईडी सीधे के बजाय कार्य करते हैं।
वॉल्ट डब्ल्यू

21
इसके लायक है कि _stop पहले से ही पायथन 3 थ्रेडिंग लाइब्रेरी में व्याप्त है। जैसे, शायद एक भिन्न चर का उपयोग करें अन्यथा आपको एक त्रुटि मिलेगी।
मृत्युंजय

113

ऐसा करने के लिए कोई आधिकारिक एपीआई नहीं है, नहीं।

आपको धागा को मारने के लिए प्लेटफ़ॉर्म एपीआई का उपयोग करने की आवश्यकता है, जैसे pthread_kill, या TerminateThread। आप ऐसे API का उपयोग कर सकते हैं जैसे कि pythonwin के माध्यम से, या ctypes के माध्यम से।

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


26
अगर जीआईएल पर सवाल है तो यह गतिरोध पैदा करेगा
मथियास उरलिचस

95

A multiprocessing.Processकर सकते हैंp.terminate()

उन मामलों में जहां मैं एक धागे को मारना चाहता हूं, लेकिन झंडे / ताले / सिग्नल / सेमाफोरस / घटनाओं / जो कुछ भी उपयोग करना नहीं चाहता हूं, मैं थ्रेड्स को पूर्ण विकसित प्रक्रियाओं को बढ़ावा देता हूं। कोड है कि सिर्फ कुछ धागे का उपयोग करता है के लिए ओवरहेड कि बुरा नहीं है।

उदाहरण के लिए, यह हेल्पर "थ्रेड्स" को आसानी से समाप्त करने के काम आता है, जो ब्लॉकिंग I / O को निष्पादित करता है

रूपांतरण तुच्छ है: संबंधित कोड में सभी threading.Threadको प्रतिस्थापित करें multiprocessing.Processऔर सभी के queue.Queueसाथ multiprocessing.Queueआवश्यक कॉल जोड़ेंp.terminate() अपनी मूल प्रक्रिया जो अपने बच्चे को मारना चाहता हैp

के लिए पायथन प्रलेखनmultiprocessing देखें ।


धन्यवाद। मैंने मल्टीप्रोसेसिंग के साथ कतार। क्यू की जगह ले ली। जॉइंटक्यूक्यू और इस जवाब का पालन किया: stackoverflow.com/a/11984760/911207
डेविड ब्रौन

इस मुद्दे पर बहुत सारे पृष्ठ। यह मुझे लगता है कि कई लोगों के लिए स्पष्ट समाधान लगता है
जियोथैरी

6
multiprocessingअच्छा है, लेकिन ध्यान रखें कि तर्कों को नई प्रक्रिया के लिए चुना जाता है। इसलिए यदि कोई एक तर्क कुछ न करने योग्य (जैसे logging.log) है तो इसका उपयोग करना एक अच्छा विचार नहीं हो सकता है multiprocessing
लाइएजर

1
multiprocessingतर्क विंडोज पर नई प्रक्रिया के लिए ले जाया जाता है, लेकिन लिनक्स उन्हें कॉपी करने के लिए फोर्किंग का उपयोग करता है (पायथन 3.7, अन्य संस्करणों को अनिश्चित करें)। तो आप उस कोड को समाप्त कर देंगे जो लिनक्स पर काम करता है लेकिन विंडोज पर अचार की त्रुटियों को बढ़ाता है।
nyanpasu64

multiprocessingलॉगिंग के साथ मुश्किल काम है। उपयोग करने की आवश्यकता है QueueHandler( इस ट्यूटोरियल को देखें )। मैंने इसे कठिन तरीके से सीखा।
फेनचेन बाओ

74

यदि आप पूरे कार्यक्रम को समाप्त करने की कोशिश कर रहे हैं, तो आप थ्रेड को "डेमॉन" के रूप में सेट कर सकते हैं। देखें Thread.daemon


इसका कोई मतलब नहीं है। दस्तावेज़ में स्पष्ट रूप से कहा गया है, "इसे शुरू करने से पहले सेट किया जाना चाहिए () कहा जाता है, अन्यथा रनटाइमईरम को उठाया जाता है।" इस प्रकार, यदि मैं एक ऐसे धागे को मारना चाहता हूं जो मूल रूप से डेमॉन नहीं था, तो मैं इसका उपयोग कैसे कर सकता हूं?
रफ़ी खाचदौरीयन

27
रफी मुझे लगता है कि वह सुझाव दे रहे हैं कि आप इसे पहले से निर्धारित कर लेंगे, यह जानते हुए कि जब आपका मुख्य धागा बाहर निकलता है तो आप भी डेमन धागे से बाहर निकलना चाहते हैं।
काल्पनिक जूल

1
सुनिश्चित नहीं हैं कि क्यों यह स्वीकार किए जाते हैं जवाब नहीं है
eric

यदि आप अपने मुख्य कार्यक्रम को बंद कर देते हैं तो भी एक धागा को एक डेमन के रूप में सेट नहीं किया जा सकता है, यदि आप चाहते हैं कि धागा चालू रहे तो भी क्या होगा?
मिशेल Piccolini

आप सर, मेरे दिन के हीरो हैं। बिल्कुल क्या मैं देख रहा था और जोड़ने के लिए कोई उपद्रव नहीं है।
Blizz

42

जैसा कि दूसरों ने उल्लेख किया है, मानदंड स्टॉप फ्लैग सेट करना है। कुछ हल्का (थ्रेड का कोई उपवर्ग नहीं, कोई वैश्विक चर नहीं) के लिए, एक लैम्ब्डा कॉलबैक एक विकल्प है। (कोष्ठकों पर ध्यान दें if stop()।)

import threading
import time

def do_work(id, stop):
    print("I am thread", id)
    while True:
        print("I am thread {} doing something".format(id))
        if stop():
            print("  Exiting loop.")
            break
    print("Thread {}, signing off".format(id))


def main():
    stop_threads = False
    workers = []
    for id in range(0,3):
        tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads))
        workers.append(tmp)
        tmp.start()
    time.sleep(3)
    print('main: done sleeping; time to stop the threads.')
    stop_threads = True
    for worker in workers:
        worker.join()
    print('Finis.')

if __name__ == '__main__':
    main()

print()एक pr()फ़ंक्शन के साथ प्रतिस्थापित करना जो हमेशा फ्लश करता है ( sys.stdout.flush()) शेल आउटपुट की शुद्धता में सुधार कर सकता है।

(केवल विंडोज / एक्लिप्स / पायथन 3.3 पर परीक्षण किया गया)


1
लिनक्स / पायथन 2.7 पर सत्यापित, एक आकर्षण की तरह काम करता है। यह आधिकारिक उत्तर होना चाहिए, यह बहुत सरल है।
पॉल केंजोरा

1
लिनक्स उबंटू सर्वर 17.10 / पायथन 3.6.3 पर सत्यापित है और यह काम करता है।
मार्कोस

1
2.7 में भी सत्यापित किया गया। इतना अच्छा जवाब!
सिल्गन

pr()कार्य क्या है ?
alper

35

यह पर आधारित है थ्रेड 2 - हत्या करने योग्य धागे (पायथन नुस्खा)

आपको PyThreadState_SetasyncExc () कॉल करने की आवश्यकता है, जो केवल ctypes के माध्यम से उपलब्ध है।

यह केवल पायथन 2.7.3 पर परीक्षण किया गया है, लेकिन यह हाल के 2.x रिलीज के साथ काम करने की संभावना है।

import ctypes

def terminate_thread(thread):
    """Terminates a python thread from another thread.

    :param thread: a threading.Thread instance
    """
    if not thread.isAlive():
        return

    exc = ctypes.py_object(SystemExit)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
        ctypes.c_long(thread.ident), exc)
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

मैं अपने धागे देने के लिए कुछ इस तरह का उपयोग कर रहा हूं KeyboardInterruptताकि उन्हें साफ करने का मौका मिले। यदि वे उसके बाद भी लटक रहे हैं, तो SystemExitउचित है, या बस एक टर्मिनल से प्रक्रिया को मार डालो।
drevicko

यह काम करता है यदि थ्रेड वर्तमान में निष्पादित हो रहा है। यह काम नहीं करता है यदि धागा एक सिस्केल में है; अपवाद को चुपचाप नजरअंदाज कर दिया जाएगा।
मथियास उरलिचस

@MatthiasUrlichs किसी भी विचार का पता लगाने के लिए कि थ्रेड निष्पादन राज्य क्या है, एक चेतावनी या रिट्री प्रिंट करने में सक्षम होने के लिए?
जोहान डाहलिन

1
@JohanDahlin आप थोड़ा इंतजार कर सकते हैं (जो, यदि आप पुन: प्रयास करना चाहते हैं, तो आपको वैसे भी करने की आवश्यकता है) और फिर isAlive () परीक्षण करें। किसी भी मामले में, जबकि यह काम करेगा, मैं यह भी गारंटी नहीं दूंगा कि यह चारों ओर झूलने वाले संदर्भों को नहीं छोड़ता है। हालांकि थ्योरी को सीपीयथॉन में सुरक्षित रूप से मारना सुरक्षित है pthread_cleanup_push()/_pop(), इसके विवेकपूर्ण उपयोग से , इसे सही ढंग से लागू करने के लिए बहुत काम होगा और यह दुभाषिया को धीमा कर देगा।
मथायस उरलिच

32

आपको कभी भी किसी के साथ सहयोग के बिना जबरन एक धागा नहीं मारना चाहिए।

एक धागा को मारना किसी भी गारंटी को हटा देता है जो कोशिश करता है / अंत में सेट होता है ताकि आप ताले को बंद कर सकें, फाइलें खुली रहें, आदि।

केवल एक बार आप यह तर्क दे सकते हैं कि थ्रेड्स को जबरन मारना एक अच्छा विचार है, किसी प्रोग्राम को तेजी से मारना है, लेकिन कभी भी सिंगल थ्रेड्स को मारना नहीं है।


11
केवल एक धागा बताना इतना कठिन क्यों है, कृपया अपने वर्तमान लूप को समाप्त करने पर खुद को मार दें ... मुझे यह नहीं मिला।
मेहंदी

2
"लूप" की पहचान करने के लिए सीपीयू में कोई तंत्र नहीं बनाया गया है, सबसे अच्छा है कि आप उम्मीद कर सकते हैं कि किसी प्रकार के सिग्नल का उपयोग किया जाए जो कोड लूप के अंदर है जो एक बार बाहर निकलने के बाद जांच करेगा। थ्रेड सिंक्रोनाइजेशन को संभालने का सही तरीका सहकारी साधनों द्वारा है, थ्रेड्स को फिर से शुरू करना, फिर से चलाना, और थ्रेड्स को मारना ऐसे कार्य हैं जो एप्लिकेशन कोड नहीं हैं।
लास वी। कार्लसन

2
@ मेहेदी: यदि मैं (व्यक्तिगत रूप से) धागे में कोड लिख रहा हूं, हां, मैं आपसे सहमत हूं। लेकिन ऐसे मामले हैं जहां मैं तीसरे पक्ष के पुस्तकालय चला रहा हूं, और मुझे उस कोड के निष्पादन पाश तक पहुंच नहीं है। अनुरोधित सुविधा के लिए यह एक उपयोग मामला है।
डैन एच

@DanH यह तीसरे पक्ष के कोड के साथ भी सबसे खराब है क्योंकि आपको पता नहीं है कि इससे क्या नुकसान हो सकता है। यदि आपकी थर्ड पार्टी लाइब्रेरी इतनी मजबूत नहीं है कि उसे मारने की आवश्यकता है, तो आपको इनमें से एक करना चाहिए: (1) लेखक से समस्या को ठीक करने के लिए कहें, (2) कुछ और उपयोग करें। यदि आपके पास वास्तव में कोई विकल्प नहीं है, तो उस कोड को एक अलग प्रक्रिया में रखना सुरक्षित होना चाहिए क्योंकि कुछ संसाधन केवल एक प्रक्रिया के भीतर साझा किए जाते हैं।
फिल 1970

25

अजगर में, आप सीधे एक धागा नहीं मार सकते हैं।

यदि आपको वास्तव में थ्रेड पैकेज का उपयोग करने के बजाय थ्रेड (!) होने की आवश्यकता नहीं है, तो मल्टीप्रोसेसिंग पैकेज का उपयोग करना है । यहां, एक प्रक्रिया को मारने के लिए, आप बस विधि को कॉल कर सकते हैं:

yourProcess.terminate()  # kill the process!

अजगर आपकी प्रक्रिया को मार देगा (यूनिक्स पर SIGTERM सिग्नल के माध्यम से, जबकि विंडोज के माध्यम से TerminateProcess() कॉल के )। एक कतार या एक पाइप का उपयोग करते समय इसका उपयोग करने के लिए ध्यान दें! (यह कतार / पाइप में डेटा को दूषित कर सकता है)

ध्यान दें कि multiprocessing.Eventऔर multiprocessing.Semaphoreकाम बिल्कुल उसी तरह से threading.Eventऔरthreading.Semaphore क्रमशः। वास्तव में, पहले वाले अक्षांशों के क्लोन हैं।

यदि आपको वास्तव में थ्रेड का उपयोग करने की आवश्यकता है, तो इसे सीधे मारने का कोई तरीका नहीं है। हालांकि, आप क्या कर सकते हैं, "डेमन थ्रेड" का उपयोग करना है । वास्तव में, पायथन में, एक धागे को डेमन के रूप में चिह्नित किया जा सकता है :

yourThread.daemon = True  # set the Thread as a "daemon thread"

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

ध्यान दें कि विधि कहा जाने daemonसे पहले थ्रेड सेट करना आवश्यक start()है!

बेशक आप कर सकते हैं, और चाहिए, के daemonसाथ भी उपयोग करें multiprocessing। यहां, जब मुख्य प्रक्रिया से बाहर निकलता है, तो यह अपने सभी शैतानी बच्चे प्रक्रियाओं को समाप्त करने का प्रयास करता है।

अंत में, कृपया ध्यान दें कि sys.exit()और os.kill()विकल्प नहीं हैं।


14

आप थ्रेड में ट्रेस स्थापित करके एक थ्रेड को मार सकते हैं जो थ्रेड से बाहर निकल जाएगा। एक संभावित कार्यान्वयन के लिए संलग्न लिंक देखें।

अजगर में एक धागे को मार डालो


मैं पहले से ही इसे देख चुका हूं। यह समाधान स्व.हल्ड फ्लैग चेक पर आधारित है
सडन डेफ

1
यहाँ कुछ जवाबों में से एक जो वास्तव में काम करता है
पोंकडूडल

5
इस समाधान के साथ दो समस्याएं: (ए) sys.settrace () के साथ एक ट्रेसर स्थापित करने से आपका थ्रेड रन धीमा हो जाएगा। यदि यह बाध्य है तो 10 गुना धीमा। (b) आपके थ्रेड को प्रभावित नहीं करेगा जबकि यह एक सिस्टम कॉल है।
मथियास उरलिचस

10

यदि आप स्पष्ट time.sleep()रूप से अपने धागे के हिस्से के रूप में कॉल कर रहे हैं (कुछ बाहरी सेवा को मतदान कहते हैं), तो फ़िलिप की विधि में सुधार event'एस में टाइमआउट का उपयोग करना है।wait() जहाँ भी आप विधिsleep()

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

import threading

class KillableThread(threading.Thread):
    def __init__(self, sleep_interval=1):
        super().__init__()
        self._kill = threading.Event()
        self._interval = sleep_interval

    def run(self):
        while True:
            print("Do Something")

            # If no kill signal is set, sleep for the interval,
            # If kill signal comes in while sleeping, immediately
            #  wake up and handle
            is_killed = self._kill.wait(self._interval)
            if is_killed:
                break

        print("Killing Thread")

    def kill(self):
        self._kill.set()

फिर इसे चलाने के लिए

t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread

आईएनजी के wait()बजाय उपयोग करने sleep()और नियमित रूप से घटना की जांच करने का लाभ यह है कि आप नींद के लंबे अंतराल में प्रोग्राम कर सकते हैं, थ्रेड लगभग तुरंत बंद हो जाता है (जब आप अन्यथा sleep()आईएनजी होंगे) और मेरी राय में, बाहर निकलने से निपटने के लिए कोड काफी सरल है ।


3
इस पोस्ट को क्यों अस्वीकृत किया गया? क्या गलत है w / यह पोस्ट? यह ठीक वैसा ही दिखता है जैसा मुझे चाहिए ....
JDOaktown

9

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


8

Thread.stopनिम्न उदाहरण कोड में दिखाए गए तरीके से इसे लागू करना निश्चित रूप से संभव है :

import sys
import threading
import time


class StopThread(StopIteration):
    pass

threading.SystemExit = SystemExit, StopThread


class Thread2(threading.Thread):

    def stop(self):
        self.__stop = True

    def _bootstrap(self):
        if threading._trace_hook is not None:
            raise ValueError('Cannot run thread with tracing!')
        self.__stop = False
        sys.settrace(self.__trace)
        super()._bootstrap()

    def __trace(self, frame, event, arg):
        if self.__stop:
            raise StopThread()
        return self.__trace


class Thread3(threading.Thread):

    def _bootstrap(self, stop_thread=False):
        def stop():
            nonlocal stop_thread
            stop_thread = True
        self.stop = stop

        def tracer(*_):
            if stop_thread:
                raise StopThread()
            return tracer
        sys.settrace(tracer)
        super()._bootstrap()

###############################################################################


def main():
    test1 = Thread2(target=printer)
    test1.start()
    time.sleep(1)
    test1.stop()
    test1.join()
    test2 = Thread2(target=speed_test)
    test2.start()
    time.sleep(1)
    test2.stop()
    test2.join()
    test3 = Thread3(target=speed_test)
    test3.start()
    time.sleep(1)
    test3.stop()
    test3.join()


def printer():
    while True:
        print(time.time() % 1)
        time.sleep(0.1)


def speed_test(count=0):
    try:
        while True:
            count += 1
    except StopThread:
        print('Count =', count)

if __name__ == '__main__':
    main()

Thread3वर्ग लगभग 33% से अधिक तेजी से कोड को चलाने के लिए प्रकट होता है Thread2वर्ग।


3
यह self.__stopथ्रेड में सेट होने के लिए चेकों को इंजेक्ट करने का एक चतुर तरीका है । ध्यान दें कि यहां अन्य समाधानों में से अधिकांश की तरह, यह वास्तव में एक अवरुद्ध कॉल को बाधित नहीं करेगा, क्योंकि ट्रेस फ़ंक्शन केवल तभी कॉल करता है जब एक नया स्थानीय क्षेत्र दर्ज किया जाता है। यह भी ध्यान देने योग्य है कि sys.settraceवास्तव में डिबगर्स, प्रोफाइल इत्यादि को लागू करने के लिए है और जैसे कि सीपीथॉन का कार्यान्वयन विवरण माना जाता है, और अन्य पायथन कार्यान्वयन में मौजूद होने की गारंटी नहीं है।
डानो

3
@ डानो: Thread2वर्ग के साथ सबसे बड़ी समस्याओं में से एक यह है कि यह लगभग दस गुना धीमा कोड चलाता है। कुछ लोगों को अभी भी यह स्वीकार्य लग सकता है।
नॉक्टिस स्काईटॉवर

इस पर +1 कोड निष्पादन को काफी धीमा कर देता है .. मैं सुझाव दूंगा कि इस समाधान के लेखक ने उत्तर में इस जानकारी को शामिल किया है।
विशाल

6
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

टी आपका हैThread वस्तु है।

अजगर स्रोत पढ़ें ( Modules/threadmodule.cऔर Python/thread_pthread.h) आप देख सकते हैं Thread.identएक pthread_tप्रकार है, इसलिए आप pthreadअजगर के उपयोग में कुछ भी कर सकते हैं libpthread


और आप विंडोज पर यह कैसे करते हैं?
आईसीएक्स

12
तुम नहीं; विंडोज पर नहीं और लिनक्स पर भी नहीं। कारण: जब आप ऐसा कर रहे हों तो प्रश्न में धागा जीआईएल को पकड़ सकता है (जब आप सी में कॉल करते हैं तो पायथन जीआईएल जारी करता है)। यदि ऐसा होता है, तो आपका कार्यक्रम तुरन्त गतिरोध कर देगा। यहां तक ​​कि अगर यह नहीं है, अंत में: ब्लॉक आदि निष्पादित नहीं किए जाएंगे, इसलिए यह एक बहुत ही असुरक्षित विचार है।
मथियास उरलिचस

6

थ्रेड को मारने के लिए निम्नलिखित वर्कअराउंड का उपयोग किया जा सकता है:

kill_threads = False

def doSomething():
    global kill_threads
    while True:
        if kill_threads:
            thread.exit()
        ......
        ......

thread.start_new_thread(doSomething, ())

इसका उपयोग थ्रेड को समाप्त करने के लिए भी किया जा सकता है, जिसका कोड मुख्य थ्रेड से दूसरे मॉड्यूल में लिखा गया है। हम उस मॉड्यूल में एक वैश्विक चर घोषित कर सकते हैं और इसका उपयोग उस मॉड्यूल में थ्रेडेड / एस को समाप्त करने के लिए कर सकते हैं।

मैं आमतौर पर प्रोग्राम से बाहर निकलने के सभी थ्रेड्स को समाप्त करने के लिए इसका उपयोग करता हूं। यह धागा / एस को समाप्त करने का सही तरीका नहीं हो सकता है, लेकिन मदद कर सकता है।


थम्स अप। समझने में सरल।
एलिसाएलियाह

6

मुझे इस खेल में देर हो गई है, लेकिन मैं एक समान प्रश्न के साथ कुश्ती कर रहा हूं और निम्नलिखित दोनों मेरे लिए पूरी तरह से मुद्दे को हल करते हैं और मुझे कुछ बुनियादी धागा राज्य की जाँच करने और सफाई करने की अनुमति देता है जब डेमनीकृत उप-धागा बाहर निकलता है:

import threading
import time
import atexit

def do_work():

  i = 0
  @atexit.register
  def goodbye():
    print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
           (i, threading.currentThread().ident))

  while True:
    print i
    i += 1
    time.sleep(1)

t = threading.Thread(target=do_work)
t.daemon = True
t.start()

def after_timeout():
  print "KILL MAIN THREAD: %s" % threading.currentThread().ident
  raise SystemExit

threading.Timer(2, after_timeout).start()

पैदावार:

0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]

यह एक उत्कृष्ट जवाब यह है कि इस सूची में अधिक होना चाहिए है
drootang

थ्रेड SystemExitपर उठाना after_timeoutमुख्य थ्रेड के लिए कुछ भी क्यों नहीं करेगा (जो कि पूर्व में इस उदाहरण से बाहर निकलने का इंतजार कर रहा है)?
डेविस हेरिंग

@DavisHerring मुझे यकीन नहीं है कि आप क्या कर रहे हैं। SystemExit मुख्य धागे को मारता है, आपको क्यों लगता है कि यह WOULDNT मुख्य धागे पर कुछ भी करेगा? उस कॉल के बिना, कार्यक्रम सिर्फ बच्चे के धागे पर इंतजार करना जारी रखेगा। आप मुख्य धागे को मारने के लिए ctrl + c या किसी अन्य माध्यम का उपयोग भी कर सकते हैं लेकिन यह एक उदाहरण है।
स्लमट्रिम्पेट

@slumtrimpet: SystemExitमें केवल दो विशेष गुण हैं: यह एक ट्रेसबैक (जब किसी भी धागे को एक फेंकने से बाहर निकलता है) का उत्पादन नहीं करता है, और यदि एक फेंकने से मुख्य धागा बाहर निकलता है तो बाहर निकलने की स्थिति निर्धारित करता है (जबकि फिर भी अन्य गैर-डीमन थ्रेड्स की प्रतीक्षा कर रहा है) बाहर निकलने के लिए)।
डेविस हेरिंग

@DavisHerring ओह, मुझे गेटा मिला। आपसे पूर्णतः सहमत हूँ। मैं गलत कर रहा था कि हमने यहां क्या किया और आपकी टिप्पणी को गलत समझा।
slumtrimpet

4

एक बात जो मैं जोड़ना चाहता हूं वह यह है कि यदि आप आधिकारिक दस्तावेज को थ्रेडिंग लेबर पायथन में पढ़ते हैं , तो यह "राक्षसी" धागे के उपयोग से बचने की सिफारिश की जाती है, जब आप थ्रेड को अचानक समाप्त नहीं करना चाहते हैं, तो उस ध्वज के साथ जिसे पाओलो रूवेल्ली ने उल्लेख किया है

आधिकारिक दस्तावेज से:

डेमन थ्रेड्स को अचानक बंद कर दिया जाता है। उनके संसाधन (जैसे खुली फाइलें, डेटाबेस लेनदेन, आदि) ठीक से जारी नहीं किए जा सकते हैं। यदि आप चाहते हैं कि आपके धागे सुशोभित रूप से बंद हो जाएं, तो उन्हें गैर-डायमोनिक बनाएं और एक घटना जैसे उपयुक्त सिग्नलिंग तंत्र का उपयोग करें।

मुझे लगता है कि डेमोनिक धागे बनाना आपके आवेदन पर निर्भर करता है, लेकिन सामान्य तौर पर (और मेरी राय में) यह बेहतर है कि उन्हें मारने या उन्हें डायमोनिक बनाने से बचें। मल्टीप्रोसेसिंग में आप is_alive()प्रक्रिया की स्थिति की जांच करने के लिए और उन्हें समाप्त करने के लिए "समाप्त" का उपयोग कर सकते हैं (साथ ही आप जीआईएल की समस्याओं से बचते हैं)। लेकिन आप और अधिक समस्याएँ पा सकते हैं, कभी-कभी, जब आप अपने कोड को विंडोज में निष्पादित करते हैं।

और हमेशा याद रखें कि यदि आपके पास "जीवित धागे" हैं, तो पायथन दुभाषिया उन्हें इंतजार करने के लिए चला रहा होगा। (इस वजह से डेमोनिक आपकी मदद कर सकता है अगर अचानक कोई फर्क नहीं पड़ता)।


4
मुझे अंतिम पैराग्राफ समझ नहीं आ रहा है।
tshepang

@Tshepang इसका मतलब है कि अगर वहाँ अपने आवेदन में किसी भी चल रहे गैर-भूत का सूत्र हैं, पायथन दुभाषिया तक चलते रहेंगे सभी गैर डेमॉन धागे कर रहे हैं किया । यदि आप परवाह नहीं करते हैं कि कार्यक्रम समाप्त होने पर थ्रेड समाप्त होता है, तो उन्हें डेमॉन बनाने का उपयोग किया जा सकता है।
टॉम मायडेल्टन

3

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


1

हालांकि यह पुराना है, यह कुछ के लिए एक उपयोगी समाधान हो सकता है:

एक छोटा मॉड्यूल जो थ्रेडिंग के मॉड्यूल की कार्यक्षमता को बढ़ाता है - एक धागे को दूसरे धागे के संदर्भ में अपवादों को बढ़ाने की अनुमति देता है। उठाकर SystemExit, आप अंततः अजगर धागे को मार सकते हैं।

import threading
import ctypes     

def _async_raise(tid, excobj):
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        # """if it returns a number greater than one, you're in trouble, 
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")

class Thread(threading.Thread):
    def raise_exc(self, excobj):
        assert self.isAlive(), "thread must be started"
        for tid, tobj in threading._active.items():
            if tobj is self:
                _async_raise(tid, excobj)
                return

        # the thread was alive when we entered the loop, but was not found 
        # in the dict, hence it must have been already terminated. should we raise
        # an exception here? silently ignore?

    def terminate(self):
        # must raise the SystemExit type, instead of a SystemExit() instance
        # due to a bug in PyThreadState_SetAsyncExc
        self.raise_exc(SystemExit)

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

हालांकि, इसके मूल स्रोत के अनुसार , इस कोड के साथ कुछ समस्याएं हैं।

  • अजगर बाइटकोड को निष्पादित करते समय अपवाद केवल उठाया जाएगा। यदि आपका थ्रेड एक देशी / अंतर्निहित अवरोधन फ़ंक्शन को कॉल करता है, तो अपवाद केवल तब उठाया जाएगा जब निष्पादन अजगर कोड पर लौट आए।
    • यदि अंतर्निहित फ़ंक्शन आंतरिक रूप से PyErr_Clear () कहता है, तो एक समस्या यह भी है कि प्रभावी रूप से आपके लंबित अपवाद को रद्द कर दिया जाएगा। आप इसे फिर से बढ़ाने की कोशिश कर सकते हैं।
  • केवल अपवाद प्रकारों को सुरक्षित रूप से उठाया जा सकता है। अपवाद उदाहरणों से अप्रत्याशित व्यवहार होने की संभावना है, और इस प्रकार प्रतिबंधित हैं।
    • उदाहरण के लिए: t1.raise_exc (TypeError) और t1.raise_exc (TypeError ("blah"))।
    • IMHO यह एक बग है, और मैंने इसे एक के रूप में रिपोर्ट किया। अधिक जानकारी के लिए, http://mail.python.org/pipermail/python-dev/2006-August/068158.html
  • मैंने इस फ़ंक्शन को अंतर्निहित थ्रेड मॉड्यूल में उजागर करने के लिए कहा, लेकिन चूंकि ctypes एक मानक पुस्तकालय (2.5 के रूप में) बन गया है, और इस
    सुविधा के कार्यान्वयन-अज्ञेय होने की संभावना नहीं है, इसलिए इसे
    अनएक्सपोज्ड रखा जा सकता है ।

0

यह विंडोज 7 पर pywin32 के साथ काम करने लगता है

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()

0

पीटर हिंटजेन्स - के संस्थापकों में से एक ØMQ -Project - कहते हैं, ØMQ का उपयोग करने और ताले, mutexes, घटनाओं आदि जैसे तुल्यकालन पुरातन से परहेज, sanest और securest तरह से मल्टी-थ्रेडेड कार्यक्रमों लिखने के लिए है:

http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ

इसमें एक बच्चा धागा बताना शामिल है, कि उसे अपना काम रद्द करना चाहिए। यह एक QMQ-सॉकेट के साथ थ्रेड को लैस करने और उस सॉकेट पर पोलिंग के लिए एक संदेश के लिए किया जाएगा जिसमें यह कहा जाए कि इसे रद्द कर देना चाहिए।

लिंक .MQ के साथ बहु-थ्रेडेड पायथन कोड पर एक उदाहरण प्रदान करता है।


0

मान लें, कि आप एक ही फ़ंक्शन के कई थ्रेड्स करना चाहते हैं, तो यह IMHO आईडी द्वारा एक को रोकने के लिए सबसे आसान कार्यान्वयन है:

import time
from threading import Thread

def doit(id=0):
    doit.stop=0
    print("start id:%d"%id)
    while 1:
        time.sleep(1)
        print(".")
        if doit.stop==id:
            doit.stop=0
            break
    print("end thread %d"%id)

t5=Thread(target=doit, args=(5,))
t6=Thread(target=doit, args=(6,))

t5.start() ; t6.start()
time.sleep(2)
doit.stop =5  #kill t5
time.sleep(2)
doit.stop =6  #kill t6

अच्छी बात यह है कि, आपके पास एक ही और विभिन्न प्रकार के कई कार्य हो सकते हैं, और उन सभी को रोक सकते हैं functionname.stop

यदि आप फ़ंक्शन का केवल एक धागा रखना चाहते हैं, तो आपको आईडी को याद रखने की आवश्यकता नहीं है। बस रोकें, अगर doit.stop> 0।


0

बस @ SCB के विचार (जो वास्तव में मुझे क्या चाहिए था) को बनाने के लिए एक अनुकूलित फ़ंक्शन के साथ एक KillableThread उपवर्ग बनाने के लिए:

from threading import Thread, Event

class KillableThread(Thread):
    def __init__(self, sleep_interval=1, target=None, name=None, args=(), kwargs={}):
        super().__init__(None, target, name, args, kwargs)
        self._kill = Event()
        self._interval = sleep_interval
        print(self._target)

    def run(self):
        while True:
            # Call custom function with arguments
            self._target(*self._args)

        # If no kill signal is set, sleep for the interval,
        # If kill signal comes in while sleeping, immediately
        #  wake up and handle
        is_killed = self._kill.wait(self._interval)
        if is_killed:
            break

    print("Killing Thread")

def kill(self):
    self._kill.set()

if __name__ == '__main__':

    def print_msg(msg):
        print(msg)

    t = KillableThread(10, print_msg, args=("hello world"))
    t.start()
    time.sleep(6)
    print("About to kill thread")
    t.kill()

स्वाभाविक रूप से, @SBC की तरह, थ्रेड रुकने के लिए नया लूप चलाने का इंतजार नहीं करता है। इस उदाहरण में, आप "किलिंग थ्रेड" संदेश को "थ्रेड को मारने के बारे में" के ठीक बाद छपे हुए देखेंगे, बजाय इसके कि थ्रेड को पूरा करने के लिए 4 और सेकंड इंतजार करें (क्योंकि हम पहले ही 6 सेकंड के लिए सो चुके हैं)।

KillableThread कंस्ट्रक्टर में दूसरा तर्क आपका कस्टम फ़ंक्शन (प्रिंट_एमएस यहां) है। आर्ग्स तर्क वे तर्क हैं जिनका उपयोग यहां फ़ंक्शन (("हैलो वर्ल्ड") को कॉल करते समय किया जाएगा।


0

जैसा कि @ Kozyarchuk के उत्तर में उल्लेख किया गया है , ट्रेस काम करता है। चूँकि इस उत्तर में कोई कोड नहीं है, यहाँ एक काम करने के लिए तैयार उदाहरण है:

import sys, threading, time 

class TraceThread(threading.Thread): 
    def __init__(self, *args, **keywords): 
        threading.Thread.__init__(self, *args, **keywords) 
        self.killed = False
    def start(self): 
        self._run = self.run 
        self.run = self.settrace_and_run
        threading.Thread.start(self) 
    def settrace_and_run(self): 
        sys.settrace(self.globaltrace) 
        self._run()
    def globaltrace(self, frame, event, arg): 
        return self.localtrace if event == 'call' else None
    def localtrace(self, frame, event, arg): 
        if self.killed and event == 'line': 
            raise SystemExit() 
        return self.localtrace 

def f(): 
    while True: 
        print('1') 
        time.sleep(2)
        print('2') 
        time.sleep(2)
        print('3') 
        time.sleep(2)

t = TraceThread(target=f) 
t.start() 
time.sleep(2.5) 
t.killed = True

यह मुद्रित होने के बाद बंद हो जाता है 1और 23छपा नहीं है।


-1

आप एक प्रक्रिया में अपनी कमांड निष्पादित कर सकते हैं और फिर प्रक्रिया आईडी का उपयोग करके इसे मार सकते हैं। मुझे दो थ्रेड में से एक के बीच सिंक करने की आवश्यकता थी, जिसमें से एक अपने आप वापस नहीं आता है।

processIds = []

def executeRecord(command):
    print(command)

    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    processIds.append(process.pid)
    print(processIds[0])

    #Command that doesn't return by itself
    process.stdout.read().decode("utf-8")
    return;


def recordThread(command, timeOut):

    thread = Thread(target=executeRecord, args=(command,))
    thread.start()
    thread.join(timeOut)

    os.kill(processIds.pop(), signal.SIGINT)

    return;

-1

सेटडैमन (ट्रू) के साथ उप धागा शुरू करें।

def bootstrap(_filename):
    mb = ModelBootstrap(filename=_filename) # Has many Daemon threads. All get stopped automatically when main thread is stopped.

t = threading.Thread(target=bootstrap,args=('models.conf',))
t.setDaemon(False)

while True:
    t.start()
    time.sleep(10) # I am just allowing the sub-thread to run for 10 sec. You can listen on an event to stop execution.
    print('Thread stopped')
    break

-2

यह एक बुरा जवाब है, टिप्पणियों को देखें

यह कैसे करना है:

from threading import *

...

for thread in enumerate():
    if thread.isAlive():
        try:
            thread._Thread__stop()
        except:
            print(str(thread.getName()) + ' could not be terminated'))

इसे कुछ सेकंड दें फिर आपका धागा बंद कर दिया जाना चाहिए। thread._Thread__delete()विधि की भी जाँच करें ।

मैं thread.quit()सुविधा के लिए एक विधि सुझाऊँगा । उदाहरण के लिए यदि आपके धागे में एक सॉकेट है, तो मैं quit()आपके सॉकेट-हैंडल क्लास में एक विधि बनाने की सलाह दूंगा, सॉकेट को समाप्त करना, फिर thread._Thread__stop()अपने अंदर चलाना quit()


मुझे अपने थ्रेडिंग के अंदर self._Thread__stop () का उपयोग करना था। इसे रोकने के लिए ऑब्जेक्ट को फैलाएं। मुझे समझ में नहीं आता है कि एक साधारण स्व.जोइन () इस उदाहरण ( code.activestate.com/recipes/65448-thread-control-idiom ) की तरह क्यों काम नहीं करता है।
harijay

12
"यह वास्तव में एक धागा बंद नहीं करता है" पर अधिक विवरण सहायक होगा।
2371

19
मूल रूप से, _Thread__stop पद्धति को कॉल करने के अलावा पायथन को यह बताने से कोई प्रभाव नहीं पड़ता कि धागा बंद है। यह वास्तव में जारी रह सकता है। उदाहरण के लिए gist.github.com/2787191 देखें ।
ब्लूहॉर्न

35
यह सादा गलत है। _Thread__stop()केवल एक धागे को रोक दिया जाता है , यह वास्तव में धागे को बंद नहीं करता है! ऐसा कभी न करें। पढ़ लिया है
dotancohen

-2

यदि आपको वास्तव में एक उप-कार्य को मारने की क्षमता की आवश्यकता है, तो वैकल्पिक कार्यान्वयन का उपयोग करें। multiprocessingऔर geventदोनों अंधाधुंध "थ्रेड" को मारने का समर्थन करते हैं।

पायथन का सूत्रण रद्द करने का समर्थन नहीं करता है। यहां तक ​​की कोशिश नहीं करते। आपके कोड में गतिरोध, भ्रष्ट या लीक मेमोरी की संभावना है, या अन्य अनपेक्षित "दिलचस्प" हार्ड-टू-डीबग प्रभाव हैं जो शायद ही कभी और नॉनडेटर्मिनिस्टली होते हैं।


2
... और हाँ, मुझे पता है कि दोनों कड़ाई से "थ्रेडिंग" नहीं कर रहे हैं, लेकिन वे दोनों काम करते हैं यदि आपका कोड उनके मॉडल पर फिट बैठता है (या फिट होने के लिए बनाया जा सकता है)।
मथायस उरलिचस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.