免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6506 | 回复: 11
打印 上一主题 下一主题

多线程下的奇怪问题,很奇怪,大家看看。 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-06-30 17:35 |只看该作者 |倒序浏览

  1. #!/usr/bin/env python
  2. # -*- coding: gb2312 -*-

  3. import threading

  4. def flush_p(string):
  5.     import sys
  6.     print string
  7.     sys.stdout.flush()

  8. class mythread(threading.Thread):
  9.     def __init__(self):
  10.         threading.Thread.__init__(self, group=None)
  11.         self.__cond = threading.Condition(threading.Lock())
  12.         self.__task = None

  13.     def run(self):
  14.         while True:
  15.             self.__cond.acquire()
  16.             while self.__task == None:
  17.                 self.__cond.wait()

  18.             if isinstance(self.__task, exit_task):
  19.                 self.__task = None
  20.                 self.__cond.release()
  21.                 flush_p('xxxxxxxxxxxxxxxxxxxx')
  22.                 break
  23.             try:
  24.                 print 'b run'
  25.                 self.__task.run()
  26.                 print 'a run'               
  27.             except:
  28.                 pass
  29.             finally:
  30.                 flush_p('mythread: b none')
  31.                 self.__task = None
  32.                 flush_p('mythread: a none')
  33.                 self.__cond.release()

  34.     def set(self, task):
  35.         self.__cond.acquire()
  36.         self.__task = task
  37.         self.__cond.notify()
  38.         self.__cond.release()

  39.     def terminate(self):
  40.         flush_p('mythread: b terminate')
  41.         self.set(exit_task())
  42.         flush_p('mythread: a terminate')
  43.         self.join()

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

  48.     def run(self):
  49.         if self.__callfunc != None:
  50.             if self.__calldata != None:
  51.                 self.__callfunc(self.__calldata)
  52.             else:
  53.                 self.__callfunc()
  54.         else:
  55.             pass

  56. class exit_task(task):
  57.     pass

  58. class wrapper:
  59.     def __init__(self):
  60.         def worker():
  61.             print 'worker'

  62.         self.myt = mythread()
  63.         self.myt.start()
  64.         self.myt.set(task(worker))

  65.     def __del__(self):
  66.         self.myt.terminate()
  67.         self.myt.join()

  68.     def __Fuck(self):
  69.         print 'fuck'

  70. def main():
  71.     w = wrapper()
  72.    

  73. if __name__ == '__main__':
  74.     main()

复制代码

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

  1. #!/usr/bin/env python
  2. # -*- coding: gb2312 -*-

  3. import threading

  4. def flush_p(string):
  5.     import sys
  6.     print string
  7.     sys.stdout.flush()

  8. class mythread(threading.Thread):
  9.     def __init__(self):
  10.         threading.Thread.__init__(self, group=None)
  11.         self.__cond = threading.Condition(threading.Lock())
  12.         self.__task = None

  13.     def run(self):
  14.         while True:
  15.             self.__cond.acquire()
  16.             while self.__task == None:
  17.                 self.__cond.wait()

  18.             if isinstance(self.__task, exit_task):
  19.                 self.__task = None
  20.                 self.__cond.release()
  21.                 flush_p('xxxxxxxxxxxxxxxxxxxx')
  22.                 break
  23.             try:
  24.                 print 'b run'
  25.                 self.__task.run()
  26.                 print 'a run'               
  27.             except:
  28.                 pass
  29.             finally:
  30.                 flush_p('mythread: b none')
  31.                 self.__task = None
  32.                 flush_p('mythread: a none')
  33.                 self.__cond.release()

  34.     def set(self, task):
  35.         self.__cond.acquire()
  36.         self.__task = task
  37.         self.__cond.notify()
  38.         self.__cond.release()

  39.     def terminate(self):
  40.         flush_p('mythread: b terminate')
  41.         self.set(exit_task())
  42.         flush_p('mythread: a terminate')
  43.         self.join()

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

  48.     def run(self):
  49.         if self.__callfunc != None:
  50.             if self.__calldata != None:
  51.                 self.__callfunc(self.__calldata)
  52.             else:
  53.                 self.__callfunc()
  54.         else:
  55.             pass

  56. class exit_task(task):
  57.     pass

  58. class wrapper:
  59.     def __init__(self):
  60.         def worker():
  61.             print 'worker'

  62.         self.myt = mythread()
  63.         self.myt.start()
  64.         self.myt.set(task(self.__Fuck))        

  65.     def __del__(self):
  66.         self.myt.terminate()
  67.         self.myt.join()

  68.     def __Fuck(self):
  69.         print 'fuck'

  70. def main():
  71.     w = wrapper()
  72.    

  73. if __name__ == '__main__':
  74.     main()
复制代码

论坛徽章:
0
2 [报告]
发表于 2007-06-30 17:41 |只看该作者
另外,如果把

  1.     def __del__(self):
  2.         self.myt.terminate()
  3.         self.myt.join()
复制代码

中的代码移到 __init__(self)中,则没有问题,以上两种情况(self.myt.set(task(worker)), self.myt.set(task(self.__Fuck)))都不会阻塞,这又是为什么呢?

论坛徽章:
0
3 [报告]
发表于 2007-07-02 09:22 |只看该作者
可能是Python并没有自动调用__del__.

论坛徽章:
0
4 [报告]
发表于 2007-07-03 09:20 |只看该作者
__del__的执行是不受你控制的。还是建议把__del__封装为其它的方法进行主动调用。

论坛徽章:
0
5 [报告]
发表于 2007-07-03 17:35 |只看该作者
只好这样了

论坛徽章:
0
6 [报告]
发表于 2007-07-03 17:48 |只看该作者
看样子,你的第二种那个情况应该是主线程没有退出。

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

  1. finally:
  2.                 flush_p('mythread: b none')
  3.                 self.__task = None
  4.                 flush_p('mythread: a none')
  5.                 self.__cond.release()
复制代码

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

于是,,死锁了。。

[ 本帖最后由 星尘细雨 于 2007-7-3 17:50 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2007-07-03 18:09 |只看该作者
尽量不要去重新定义__del__, 因为你很难控制它在什么时候调用,在程序中估计一个对象什么时候引用计数为0是一件很困难的事情。

论坛徽章:
0
8 [报告]
发表于 2007-07-03 23:24 |只看该作者
原帖由 星尘细雨 于 2007-7-3 17:48 发表
看样子,你的第二种那个情况应该是主线程没有退出。

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

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


是的,__del__必然是会调用的,但是为什么self.__task = None会使wrapper对象销毁呢?

论坛徽章:
0
9 [报告]
发表于 2007-07-03 23:37 |只看该作者
self.__task = None 会造成对self.__task的销毁,
因为self.__task里面引用了一个warpper的方法,
所以当销毁task的时候,warpper的引用计数也会减一的。
当引用计数值为0的时候对象就会被销毁。

通常很难计算对象在什么地方销毁的。

论坛徽章:
0
10 [报告]
发表于 2007-07-04 08:42 |只看该作者
那么如果我把wrapper的__init__最后添加:
import time
time.sleep(0)
就没有问题,这又是为什么呢?
  1.     def __init__(self):

  2.         def worker():

  3.             print 'worker'



  4.         self.myt = mythread()

  5.         self.myt.start()

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

  7.         import time

  8.         time.sleep(0)
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP