您的位置:首页 > 编程语言 > Python开发

python自定义线程类的使用与共享全局变量的问题

2019-07-03 15:35 1106 查看

       通过使用threading模块能完成多任务的程序开发,但 实际开发中为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承

threading.Thread
就可以了,然后重写父类的
run
方法即可

1.自定义线程类

[code]import time

class Test_Thread(threading.Thread):  #1.自定义线程类,继承threading.Thread
def run(self):  #2.重写父类的run方法
for i in range(3):
time.sleep(1)
msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
print(msg)

if __name__ == '__main__':
t = Test_Thread() #创建线程对象
t.start()  #这里的start方法是父类Threading.Thread里面的方法,start()会调用run方法(重写的run)
'''
I'm Thread-1 @ 0
I'm Thread-1 @ 1
I'm Thread-1 @ 2
'''

     python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。

2.注意线程的执行顺序

多线程执行时,执行顺序是由cpu进行调度,所以下面程序每次执行 的顺序几乎都不一致。

[code]import threading
import time

class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm "+self.name+' @ '+str(i) #self.name获取线程的名称
print(msg)
def test():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
test()
''' 可以看出5个线程同时执行,同时执行5个run里面的循环,但每次循环执行的时间大体一致
I'm Thread-3 @ 0
I'm Thread-1 @ 0
I'm Thread-2 @ 0
I'm Thread-5 @ 0
I'm Thread-4 @ 0
I'm Thread-1 @ 1
I'm Thread-3 @ 1
I'm Thread-2 @ 1
I'm Thread-5 @ 1
I'm Thread-4 @ 1
I'm Thread-3 @ 2
I'm Thread-2 @ 2
I'm Thread-1 @ 2
I'm Thread-5 @ 2
I'm Thread-4 @ 2
'''

     从代码和执行结果我们可以看出,多线程程序的执行顺序是不确定的。当执行到sleep语句时,线程将被阻塞(Blocked),到sleep结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。上面的代码中只能保证每个线程都运行完整个run函数,但是线程的启动顺序、run函数中每次循环的执行顺序都不能确定。

总结

  1. 每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。
  2. 当线程的run()方法结束时该线程完成。
  3. 无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。

3.python中线程共享全局变量的问题

3.1实现线程之间共享全局变量的方式

[code]from threading import Thread
import time

num = 100
ls1 = [9,8,7]

def work1():
global num
for i in range(3):
num += 1
ls1.append(i)

print("----in work1, num is %d---"%num)

def work2():
global num  # 注意这里num是全局不可变对象变量,所以在函数里修改它需要声明,否则报错。
print("----in work2, num is %d---"%num)
print("----in work2, ls1 is :"+str(ls1)) #注意这里ls1是全局可变对象变量,所以在函数里修改它不用声明

'''1.在一个函数中对全局变量进行修改时,如果是不可变对象包括int,float,string,tuple等,则需要对该全局变量用global声明
2.如果该全局变量是可变对象包括list,dict,自定义类的实例等,则不需要用global进行声明。
'''

print("---线程创建之前num is %d---"%num)
print("---线程创建之前num is %d---"+str(ls1))

t1 = Thread(target=work1)
t1.start()

time.sleep(2) #延时一会,保证t1线程中的事情做完
t2 = Thread(target=work2)
t2.start()

'''
---线程创建之前num is 100---
---线程创建之前num is %d---[9, 8, 7]
----in work1, num is 103---
----in work2, num is 103---
----in work2, ls1 is :[9, 8, 7, 0, 1, 2]
'''

要点总结:

  1. 在一个函数中对全局变量进行修改时,如果是不可变对象包括int,float,string,tuple等,则需要对该全局变量用global声明
  2. 如果该全局变量是可变对象包括list,dict,自定义类的实例等,则不需要用global进行声明。
  3. 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
  4. 缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

3.2多线程共享去全局变量存在的风险

[code]import threading
import time

g_num = 0

def work1(num):
global g_num
for i in range(num):
g_num += 1
print("----in work1, g_num is %d---"%g_num)

def work2(num):
global g_num
for i in range(num):
g_num += 1
print("----in work2, g_num is %d---"%g_num)

print("---线程创建之前g_num is %d---"%g_num)

t1 = threading.Thread(target=work1, args=(1000000,))
#给线程执行的函数传参,注意这里args只是变量名,可以自己定义。
t1.start()

t2 = threading.Thread(target=work2, args=(1000000,))
t2.start()

while len(threading.enumerate()) != 1:
time.sleep(1)

print("2个线程对同一个全局变量操作之后的最终结果是:%s" % g_num)

'''注意,如果参数很小的话,数据可能准备,但是参数很大的时候就会出错。
---线程创建之前g_num is 0---
----in work1, g_num is 1279887---
----in work2, g_num is 1425013---
2个线程对同一个全局变量操作之后的最终结果是:1425013
'''
  • 如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确。所以对于多线程共享全局变量存在一定的风险。当然后续有相应的解决方案,如锁的实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: