파이톤의 아름다움 [채소새부터 고수까지] - threading daemon 라인 원리 해독

4178 단어
사건의 원인은 내가 다음 코드를 보고 겪은 의혹이다. 분명히while True인데 왜 코드가 사순환하지 않았을까??
class D(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
    def run(self):
        while  True:
            url = self.queue.get()
            self.download_file(url)
            self.queue.task_done()

    def download_file(self, url):
        h = urllib2.urlopen(url)
        f = os.path.basename(url)+'.html'
        with open(f,'wb') as f:
            while  True:
                c = h.read(1024)
                if not c:
                    break
                f.write(c)

if __name__ == "__main__":
    urls= ['http://www.baidu.com','http://www.sina.com']
    queue = Queue.Queue()
    for i in range(5):
        t = D(queue)
        t.setDaemon(True)
        t.start()

    for u in urls:
        queue.put(u)

    queue.join()

그동안 setDaemon은 백엔드 라인으로 설정된 것으로 간단히 생각했지만 그 의미를 더 이상 발굴하지 못했다.
문제는 setDaemon입니다. 밑에 있는thread 모듈에서 주 루틴이 끝나면 모든 다른 루틴이 끝납니다. 이것은 주 루틴이 끝나면python이 실행 환경을 없애고 주 루틴이 끝날 것입니다.
threading 모듈의 루틴 setDaemon은 이 문제를 해결하기 위한 것입니다. 만약 setDaemon(True)이라면 이전과 같이 메인 루틴이 끝나면 모든 하위 루틴이 끝납니다.만약 setDaemon (False) 이라면, 메인 라인은 이 라인이 끝날 때까지 기다릴 것입니다. 이것은 당신이 라인을 호출하는join 방법과 같습니다.
따라서 위의 setDaemon 주석을 False로 수정하면 프로그램이 순환합니다.
사실 우리는 위의 방법을 추천하지 않는다. 위의 방법은 약간의 스레드 탱크 맛이 있지만, 만약python의 스레드 탱크 실현을 보았다면 while True
순환 중 분명히 검출 종료 문장이 있습니다.python의 세계에서는 은밀한 것보다 더 많은pythonic가 있기 때문입니다.그러나 불행히도 위의 코드가 왔다
<<고품질 코드 작성: 파이톤 프로그램 개선에 대한 91가지 조언>>>과 함께 나는 이 책을 뿌리지 않았지만, 코드의 예를 들면 확실히 의논이 필요하다고 생각한다.
setDaemon(False)이 어떻게 라인join과 같은지 궁금하시겠지만,,급하지 않으니 내 말을 천천히 들어라.
이 문제가 해결되지 않았습니다.threading 모듈이 도입되었습니다MainThread 객체
# Special thread class to represent the main thread
# This is garbage collected through an exit handler

class _MainThread(Thread):

    def __init__(self):
        Thread.__init__(self, name="MainThread")
        self._Thread__started.set()
        self._set_ident()
        with _active_limbo_lock:
            _active[_get_ident()] = self

    def _set_daemon(self):
        return False

    def _exitfunc(self):
        self._Thread__stop()
        t = _pickSomeNonDaemonThread()
        if t:
            if __debug__:
                self._note("%s: waiting for other threads", self)
        while t:
            t.join()
            t = _pickSomeNonDaemonThread()
        if __debug__:
            self._note("%s: exiting", self)
        self._Thread__delete()

def _pickSomeNonDaemonThread():
    for t in enumerate():
        if not t.daemon and t.is_alive():
            return t
    return None

# Create the main thread object,
# and make it available for the interpreter
# (Py_Main) as threading._shutdown.

_shutdown = _MainThread()._exitfunc

사실MainThread는 아무것도 하지 않았습니다. 유일한 공헌은threading 모듈을 가져올 때 실례를 만들고exitfunc에 할당shutdown 함수입니다.exitfunc는 비daemon과 alive의 모든 라인을 수집하고 라인의join 방법을 호출합니다.아, 그렇구나.
_MainThread는 조용히 막후에서 분투하고 있는데, 남은 문제는 누가 호출하는가shutdown 함수는요?
python이 실행을 없애기 전에 호출될 것입니다.pythonrun을 엽니다.c, 당신은 다음과 같은 함수를 발견할 수 있다
/* Wait until threading._shutdown completes, provided
   the threading module was imported in the first place.
   The shutdown routine will wait until all non-daemon
   "threading" threads have completed. */
static void
wait_for_thread_shutdown(void)
{
#ifdef WITH_THREAD
    PyObject *result;
    PyThreadState *tstate = PyThreadState_GET();
    PyObject *threading = PyMapping_GetItemString(tstate->interp->modules,
                                                  "threading");
    if (threading == NULL) {
        /* threading not imported */
        PyErr_Clear();
        return;
    }
    result = PyObject_CallMethod(threading, "_shutdown", "");
    if (result == NULL)
        PyErr_WriteUnraisable(threading);
    else
        Py_DECREF(result);
    Py_DECREF(threading);
#endif
}

이 녀석이 꿍꿍이를 꾸미고 있었구나. 식견이 넓어졌구나. C에서py 코드를 호출할 필요가 있었구나.어쩔 수 없지,누가 threading 모듈을 순수py 코드로 만들었어!!!

좋은 웹페이지 즐겨찾기