python线程怎么销毁

Python014

python线程怎么销毁,第1张

【Python】线程的创建、执行、互斥、同步、销毁

还是《【Java】利用synchronized(this)完成线程的临界区》(点击打开链接)、《【Linux】线程互斥》(点击打开链接)、《【C++】Windows线程的创建、执行、互斥、同步、销毁》(点击打开链接)中的设置多个线程对一个ticket进行自减操作,用来说明Python中多线程的运用,涉及的创建、执行、互斥、同步、销毁问题。

运行结果如下,还是差不多,运行三次,每次的运行结果,每个线程最终的得票结果是不同的,但是4个线程最终“得票”的总和为 ticket 最初设置的值为100000,证明这4个线程成功实现了互斥。

虽然每次运行结果是不同,但是可以看得出每次运行结果大抵上是平均的。貌似Python对线程作系统资源的处理,比Java要好。

然而,Python总要实现多线程,代码并不像想象中简单,具体如下:

[python] view plain copy print?在CODE上查看代码片派生到我的代码片

# -*-coding:utf-8-*-

import threading

mutex_lock = threading.RLock() # 互斥锁的声明

ticket = 100000 # 总票数

# 用于统计各个线程的得票数

ticket_for_thread1 = 0

ticket_for_thread2 = 0

ticket_for_thread3 = 0

ticket_for_thread4 = 0

class myThread(threading.Thread): # 线程处理函数

def __init__(self, name):

threading.Thread.__init__(self) # 线程类必须的初始化

self.thread_name = name # 将传递过来的name构造到类中的name

def run(self):

# 声明在类中使用全局变量

global mutex_lock

global ticket

global ticket_for_thread1

global ticket_for_thread2

global ticket_for_thread3

global ticket_for_thread4

while 1:

mutex_lock.acquire() # 临界区开始,互斥的开始

# 仅能有一个线程↓↓↓↓↓↓↓↓↓↓↓↓

if ticket >0:

ticket -= 1

# 统计哪到线程拿到票

print "%s抢到了票!票还剩余:%d。" % (self.thread_name, ticket)

if self.thread_name == "线程1":

ticket_for_thread1 += 1

elif self.thread_name == "线程2":

ticket_for_thread2 += 1

elif self.thread_name == "线程3":

ticket_for_thread3 += 1

elif self.thread_name == "线程4":

ticket_for_thread4 += 1

else:

break

# 仅能有一个线程↑↑↑↑↑↑↑↑↑↑↑↑

mutex_lock.release() # 临界区结束,互斥的结束

mutex_lock.release() # python在线程死亡的时候,不会清理已存在在线程函数的互斥锁,必须程序猿自己主动清理

print "%s被销毁了!" % (self.thread_name)

# 初始化线程

thread1 = myThread("线程1")

thread2 = myThread("线程2")

thread3 = myThread("线程3")

thread4 = myThread("线程4")

# 开启线程

thread1.start()

thread2.start()

thread3.start()

thread4.start()

# 等到线程1、2、3、4结束才进行以下的代码(同步)

thread1.join()

thread2.join()

thread3.join()

thread4.join()

print "票都抢光了,大家都散了吧!"

print "=========得票统计========="

print "线程1:%d张" % (ticket_for_thread1)

print "线程2:%d张" % (ticket_for_thread2)

print "线程3:%d张" % (ticket_for_thread3)

print "线程4:%d张" % (ticket_for_thread4)

1、从上面的代码可以看出,在Python2.7中要使用线程必须使用threading而不是古老的thread模块。

如果你像网上部分遗留依旧的文章一样,在Python2.7中使用thread来实现线程,至少在Eclipse的Pydev中会报错:sys.excepthook is missing,lost sys.stderr如下图所示:

所以必须使用现时Python建议使用的threading。

2、与其它编程语言类似,声明一个互斥锁,与一系列的得票数。之后,与Java同样地,Python实现线程的函数,是要重写一个类。而类中使用全局变量,则与同为脚本语言的PHP一样《【php】global的使用与php的全局变量》(点击打开链接),要用global才能使用这个全局变量,而不是C/C++可以直接使用。

3、需要注意的,Python需要在线程跑完class myThread(threading.Thread)这个类的def run(self)方法之前,必须自己手动清理互斥锁,它不会像其它编程语言那样,说线程跑完def run(self)方法,会自然而然地清理该线程被创建的互斥锁。如果没有最后一句手动清理互斥锁,则会造成死锁。

4、最后与其它编程语言一样了,利用线程的join方法可以等待这个线程跑完def run(self)方法中的所有代码,才执行之后的代码,实现同步。否则主函数中的代码,相当于与父线程。主函数开启的线程,相当于其子线程,互不影响的。

为了让代码能够并发执行,向创建线程并在核实的时候销毁它。

由于目的比较单纯,只是讲解基础的线程创建方法,所以可以直接使用threading库中的Thread类来实例化一个线程对象。

例子,用户输入两个数字,并且求其两个数字的四则运算的结果:

除了以上的一些功能以外,在python线程

中没有其他的诸如给线程发信号、设置线程调度属性、执行任何其他高级操作的功能了,如果需要这些功能,就需要手工编写了。

另外,需要注意的是,由于GIL(全局解释器锁)的存在,限制了在python解释器当中只允许运行一个线程。基于这个原因,不停该使用python线程来处理计算密集型的任务,因为在这种任务重我们希望在多个CPU核心上实现并行处理。Python线程更适合于IO处理以及设计阻塞操作的并发执行任务(即等待IO响应或等待数据库取出结果等)。

如何判断线程是否已经启动?

目的:我们加载了一个线程,但是想要知道这个线程什么时候才会开始运行?

方法:

线程的核心特征我认为就是不确定性,因为其什么时候开始运行,什么时候被打断,什么时候恢复执行,这不是程序员能够控制的,而是有系统调度

来完成的。如果遇到像某个线程的运行依托于其他某个线程运行到某个状态时该线程才能开始运行,那么这就是线程同步

问题,同样这个问题非常棘手。要解决这类问题我们要借助threading中的Event对象。

Event其实和条件标记类似,匀速线程

等待某个时间发生。初始状态时事件被设置成0。如果事件没有被设置而线程正在等待该事件,那么线程就会被阻塞,直到事件被设置位置,当有线程设置了这个事件之后,那么就会唤醒正在等待事件的线程,如果线程等待的事件已经设置了,那么线程会继续执行。

一个例子:

如上能够确定的是,主线程会在线程t运行结束时再运行。

以下用一个简单例子来表现如何用signal/slot信号槽来退出线程。

若有一个按钮,点击开始线程,再次点击退出线程,线程的工作为打印a(1-20)然后b(1-20),线程代码如下:

[python] view plain copy

class UpdateThread(QThread):

def __init__(self, parent=None):

super(UpdateThread, self).__init__(parent)

self.flag = 1# 用来判断循环是否继续的标志,通过改变该标志来使得线程中run函数退出

def run(self):

table = ['a', 'b', 'c', 'd ', 'e', 'f', 'g']

for i in range(6):

if self.flag == 1:

print table[i]

for m in range(20):

print m

time.sleep(0.5)

else:

break

print 'close'# 输出标明线程run函数已经退出

def stop(self):

print 'setting flag false'

self.flag = 0

print self.flag

一开始因为python的threading没有线程退出的api,了解到QThread有实现线程的阻塞,退出,强制退出等api,于是就将线程继承了QThread,但是在gui界面的按钮逻辑中写上mythread.wait()或者是quit()还是terminate()都无法对线程产生影响(可能是当时没有使用信号槽机制,所以不起作用),然后自己来写一个stop函数用来修改线程中的一个标志,使得run函数中的循环停止,然后就如上代码所示,在按钮逻辑中如下所写(错误示范):

[python] view plain copy

def update_db():# error

self.new_thread1 = UpdateDbThread()

if self.update_tag == 0:

self.update_tag = 1

self.new_thread1.start()

else:

self.update_tag = 0

self.new_thread1.stop()

运行之后发现stop函数虽然改变了self.flag的值,但是循环中判断语句中flag的值依然为1,循环并没有停止,然后就去查阅资料,以及上stackoverflow寻找答案,最终知道了线程需要通过信号槽机制来响应,所以便去查阅qt的signal/slot的相关信息,修改按钮代码如下:

[python] view plain copy

class mywidget(Qwidget):

sin = pyqtSignal()# 提前申明

def __init__(self):

...

self.new_thread1 = UpdateDbThread()

self.sin.connect(self.new_thread1.stop)

def update_db():

if self.update_tag == 0:

self.update_tag = 1

self.new_thread1.start()

else:

self.update_tag = 0

self.sin.emit()

[python] view plain copy

</pre><p></p><pre>

pyqtSignal信号的定义不可以写在update_db方法中,也不可以写在init()初始化方法中,原因如下(来自stackoverflow):

所以我们就在init方法之前定义好信号sin,然后连接上线程的stop方法。点击按钮发送信号,就好改变线程的标志,然后从循环中退出,运行结果如下:

为了便于贴图,我将循环输出改为了10:

线程正常退出,目标达成。可以根据自己的需要重写run方法。

还有需要注意的是,不可以在方法中写上 mythread = update_db() ,这样在运行时会报错,destroyed while thread is running,因为在方法中mythread是一个局部变量,当方法结束时,python的垃圾回收机制就会自动销毁,然后就会出现以上错误,所以必须在mythread的前面加上self,使得线程成为全局变量。