多线程下的奇怪问题,很奇怪,大家看看。

多线程下的奇怪问题,很奇怪,大家看看。



[Copy to clipboard] [ - ]
CODE:
#!/usr/bin/env python
# -*- coding: gb2312 -*-

import threading

def flush_p(string):
    import sys
    print string
    sys.stdout.flush()

class mythread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self, group=None)
        self.__cond = threading.Condition(threading.Lock())
        self.__task = None

    def run(self):
        while True:
            self.__cond.acquire()
            while self.__task == None:
                self.__cond.wait()

            if isinstance(self.__task, exit_task):
                self.__task = None
                self.__cond.release()
                flush_p('xxxxxxxxxxxxxxxxxxxx')
                break
            try:
                print 'b run'
                self.__task.run()
                print 'a run'               
            except:
                pass
            finally:
                flush_p('mythread: b none')
                self.__task = None
                flush_p('mythread: a none')
                self.__cond.release()

    def set(self, task):
        self.__cond.acquire()
        self.__task = task
        self.__cond.notify()
        self.__cond.release()

    def terminate(self):
        flush_p('mythread: b terminate')
        self.set(exit_task())
        flush_p('mythread: a terminate')
        self.join()

class task:
    def __init__(self, callfunc=None, calldata=None):
        self.__callfunc = callfunc
        self.__calldata = calldata

    def run(self):
        if self.__callfunc != None:
            if self.__calldata != None:
                self.__callfunc(self.__calldata)
            else:
                self.__callfunc()
        else:
            pass

class exit_task(task):
    pass

class wrapper:
    def __init__(self):
        def worker():
            print 'worker'

        self.myt = mythread()
        self.myt.start()
        self.myt.set(task(worker))

    def __del__(self):
        self.myt.terminate()
        self.myt.join()

    def __Fuck(self):
        print 'fuck'

def main():
    w = wrapper()
   

if __name__ == '__main__':
    main()

以上代码没有问题,但是如果把 self.myt.set(task(worker) 的worker换成类的成员函数(self.myt.set(task(self.__Fuck))    ),就会出现阻塞在self.__task = None 这块,一直不返回,
为什么?
即以下代码会阻塞:

[Copy to clipboard] [ - ]
CODE:
#!/usr/bin/env python
# -*- coding: gb2312 -*-

import threading

def flush_p(string):
    import sys
    print string
    sys.stdout.flush()

class mythread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self, group=None)
        self.__cond = threading.Condition(threading.Lock())
        self.__task = None

    def run(self):
        while True:
            self.__cond.acquire()
            while self.__task == None:
                self.__cond.wait()

            if isinstance(self.__task, exit_task):
                self.__task = None
                self.__cond.release()
                flush_p('xxxxxxxxxxxxxxxxxxxx')
                break
            try:
                print 'b run'
                self.__task.run()
                print 'a run'               
            except:
                pass
            finally:
                flush_p('mythread: b none')
                self.__task = None
                flush_p('mythread: a none')
                self.__cond.release()

    def set(self, task):
        self.__cond.acquire()
        self.__task = task
        self.__cond.notify()
        self.__cond.release()

    def terminate(self):
        flush_p('mythread: b terminate')
        self.set(exit_task())
        flush_p('mythread: a terminate')
        self.join()

class task:
    def __init__(self, callfunc=None, calldata=None):
        self.__callfunc = callfunc
        self.__calldata = calldata

    def run(self):
        if self.__callfunc != None:
            if self.__calldata != None:
                self.__callfunc(self.__calldata)
            else:
                self.__callfunc()
        else:
            pass

class exit_task(task):
    pass

class wrapper:
    def __init__(self):
        def worker():
            print 'worker'

        self.myt = mythread()
        self.myt.start()
        self.myt.set(task(self.__Fuck))        

    def __del__(self):
        self.myt.terminate()
        self.myt.join()

    def __Fuck(self):
        print 'fuck'

def main():
    w = wrapper()
   

if __name__ == '__main__':
    main()

另外,如果把

[Copy to clipboard] [ - ]
CODE:
    def __del__(self):
        self.myt.terminate()
        self.myt.join()

中的代码移到 __init__(self)中,则没有问题,以上两种情况(self.myt.set(task(worker)), self.myt.set(task(self.__Fuck)))都不会阻塞,这又是为什么呢?
可能是Python并没有自动调用__del__.
__del__的执行是不受你控制的。还是建议把__del__封装为其它的方法进行主动调用。
只好这样了
看样子,你的第二种那个情况应该是主线程没有退出。

原因应该是在加锁上。__del__必然是会被调用的。
看看你这段代码:

[Copy to clipboard] [ - ]
CODE:
finally:
                flush_p('mythread: b none')
                self.__task = None
                flush_p('mythread: a none')
                self.__cond.release()

这里的 self.__task=None会造成wrapper对象的销毁,于是wrapper就会调用mythread的terminate,
而它里面还有一句 self.set(exit_task()) , 这个也是需要加锁的!
python会在当前线程中来处理对象销毁而不管对象是在哪个线程中创建的!
这样,这个销毁warpper对象的动作因为mythread中的set需要获得锁,而锁是在
self.__task=None之后才能释放!

于是,,死锁了。。

尽量不要去重新定义__del__, 因为你很难控制它在什么时候调用,在程序中估计一个对象什么时候引用计数为0是一件很困难的事情。


QUOTE:
原帖由 星尘细雨 于 2007-7-3 17:48 发表
看样子,你的第二种那个情况应该是主线程没有退出。

原因应该是在加锁上。__del__必然是会被调用的。
看看你这段代码:

finally:
                flush_p('mythread: b none')
                self. ...

是的,__del__必然是会调用的,但是为什么self.__task = None会使wrapper对象销毁呢?
self.__task = None 会造成对self.__task的销毁,
因为self.__task里面引用了一个warpper的方法,
所以当销毁task的时候,warpper的引用计数也会减一的。
当引用计数值为0的时候对象就会被销毁。

通常很难计算对象在什么地方销毁的。
那么如果我把wrapper的__init__最后添加:
import time
time.sleep(0)
就没有问题,这又是为什么呢?

[Copy to clipboard] [ - ]
CODE:
    def __init__(self):

        def worker():

            print 'worker'



        self.myt = mythread()

        self.myt.start()

        self.myt.set(task(self.__Fuck))

        import time

        time.sleep(0)