进程无法退出总结

问题记录:
jython 运行某个脚本不会退出,通过jstack发现主线程等在Thread.join()上:

"MainThread" prio=10 tid=0x000000005891a000 nid=0x7d6d in Object.wait() [0x0000000041f5d000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000f7c50b18> (a org.python.core.FunctionThread)
    at java.lang.Thread.join(Thread.java:1186)
    - locked <0x00000000f7c50b18> (a org.python.core.FunctionThread)
    at java.lang.Thread.join(Thread.java:1239)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
    at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
    at org.python.core.PyObject.__call__(PyObject.java:403)
    at org.python.core.PyObject.__call__(PyObject.java:407)
    at org.python.core.PyMethod.__call__(PyMethod.java:121)
    at threading$py.join$22(/duitang/dist/sys/jython/Lib/threading.py:126)
    at threading$py.call_function(/duitang/dist/sys/jython/Lib/threading.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:127)
    at org.python.core.PyFunction.__call__(PyFunction.java:347)
    at org.python.core.PyMethod.__call__(PyMethod.java:121)
    at threading$py._MainThread__exitfunc$40(/duitang/dist/sys/jython/Lib/threading.py:252)
    at threading$py.call_function(/duitang/dist/sys/jython/Lib/threading.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:194)
    at org.python.core.PyFunction.__call__(PyFunction.java:417)
    at org.python.core.PyMethod.instancemethod___call__(PyMethod.java:232)
    at org.python.core.PyMethod.__call__(PyMethod.java:223)
    at org.python.core.PyMethod.__call__(PyMethod.java:218)
    at org.python.core.PyObject._callextra(PyObject.java:543)
    at atexit$py._run_exitfuncs$1(/duitang/dist/sys/jython/Lib/atexit.py:34)
    at atexit$py.call_function(/duitang/dist/sys/jython/Lib/atexit.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:120)
    at org.python.core.PyFunction.__call__(PyFunction.java:337)
    at org.python.core.PyFunction.__call__(PyFunction.java:332)
    at org.python.core.PySystemState.callExitFunc(PySystemState.java:555)
    at org.python.util.PythonInterpreter.cleanup(PythonInterpreter.java:344)
    at org.python.util.jython.run(jython.java:383)
    at org.python.util.jython.main(jython.java:137)

查看thread代码发现,进程退出的时候会通过atexit触发__exitfunc,内部实现就是等待所有非Daemon执行完毕。

class _MainThread(Thread):
    def __init__(self):
        Thread.__init__(self, name="MainThread")
        import atexit
        atexit.register(self.__exitfunc)

    def _create_thread(self):
        return java.lang.Thread.currentThread()

    def _set_daemon(self):
        return False

    def __exitfunc(self):
        _unregister_thread(self._thread)
        t = _pickSomeNonDaemonThread()
        while t:
            t.join()
            t = _pickSomeNonDaemonThread()

 
通过代码定位发现是stomp启动时会启动一个线程开启read_loop,代码如下:

def start(self):
    """
    Start the connection. This should be called after all
    listeners have been registered. If this method is not called,
    no frames will be received by the connection.
    """
    self.__running = True
    self.__attempt_connection()
    thread = self.create_thread_fc(self.__receiver_loop)
    self.__notify('connecting')

通过jstack也可以看到:
"Thread-1" prio=10 tid=0x00000000589e8800 nid=0x7d8f runnable [0x0000000041638000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.FileDispatcher.read0(Native Method)
    at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:21)
    at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:198)
    at sun.nio.ch.IOUtil.read(IOUtil.java:171)
    at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:243)
    - locked <0x00000000f7c43b30> (a java.lang.Object)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
    at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
    at org.python.core.PyObject.__call__(PyObject.java:420)
    at org.python.core.PyObject.__call__(PyObject.java:424)
    at org.python.core.PyMethod.__call__(PyMethod.java:136)
    at socket$py._do_read_nio$49(/duitang/dist/sys/jython/Lib/socket.py:471)
    at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:149)
    at org.python.core.PyFunction.__call__(PyFunction.java:357)
    at org.python.core.PyMethod.__call__(PyMethod.java:136)
    at socket$py.read$52(/duitang/dist/sys/jython/Lib/socket.py:486)
    at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:149)
    at org.python.core.PyFunction.__call__(PyFunction.java:357)
    at org.python.core.PyMethod.__call__(PyMethod.java:136)
    at socket$py.recv$153(/duitang/dist/sys/jython/Lib/socket.py:1327)
    at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
    at org.python.core.PyFunction.function___call__(PyFunction.java:406)
    at org.python.core.PyFunction.__call__(PyFunction.java:401)
    at org.python.core.PyFunction.__call__(PyFunction.java:396)
    at org.python.core.PyObject._callextra(PyObject.java:543)
    at socket$py.map_exception$27(/duitang/dist/sys/jython/Lib/socket.py:171)
    at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
    at org.python.core.PyFunction.function___call__(PyFunction.java:406)
    at org.python.core.PyFunction.__call__(PyFunction.java:401)
    at org.python.core.PyFunction.__call__(PyFunction.java:396)
    at org.python.core.PyObject._callextra(PyObject.java:543)
    at socket$py.set_last_error$29(/duitang/dist/sys/jython/Lib/socket.py:183)
    at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:141)
    at org.python.core.PyFunction.__call__(PyFunction.java:357)
    at org.python.core.PyMethod.__call__(PyMethod.java:136)
    at stomp.connect$py._Connection__read$30(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py:851)
    at stomp.connect$py.call_function(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py)
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:134)
    at org.python.core.PyFunction.__call__(PyFunction.java:347)
    at org.python.core.PyMethod.__call__(PyMethod.java:121)
    at stomp.connect$py._Connection__receiver_loop$28(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py:756)
    at stomp.connect$py.call_function(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py)

改进措施:
mqclient新增一个atexit.register(mqclient.stop)

总结:
1. 后台线程需要管理好自己的生命周期,提供stop接口
2. 线程应该daemon还是非daemon需要考虑一下,一般不可打断的线程应该设计为非daemon的,通过exit钩子退出。

 

你可能感兴趣的:(总结)