线程的理解与习题
python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用。(下面所有的测试都是py3.5版本的)
例1 使用多线程并发的操作,花费时间要短很多
import threading
import time
def run():
time.sleep(1)
print("跑步")
if __name__ == '__main__':
for i in range(5):
t = threading.Thread(target = run)
t.start()
说明 :
threading是一个对象
threading.Thread创建一个对象,这里循环了5次就是创建了五个对象并且五个对象一起执行。当调用Start()方法的时候就开始执行了 会看到程序运行很快
运行的结果
跑步
跑步
跑步
跑步
跑步
例2:主线程会等着子线程的运行
import threading
import time
from time import ctime
def run():
time.sleep(1)
print("跑步")
def eat():
time.sleep(1)
print("吃饭")
if __name__ == '__main__':
print("开始的时间%s"%ctime()) #字符串的格式显示时间
for i in range(3):
t = threading.Thread(target=run) # 创建3个线程
e = threading.Thread(target=eat) # 创建3个线程
t.start()
e.start()
#time.sleep(3) # 主线程要是不加延时会马上运行过去等着子线程运行
print("结束时间的时间%s"%ctime())
例3 查看当前的线程数量 threading.enumerate() 当前的所有线程
import threading
import time
from time import ctime
def run():
time.sleep(1)
print("跑步")
def eat():
time.sleep(1)
print("吃饭")
if __name__ == '__main__':
print("开始的时间%s"%ctime()) #字符串的格式显示时间
t = threading.Thread(target=run) # 创建1个线程
e = threading.Thread(target=eat) # 创建1个线程
t.start() #线程开始执行
e.start() #线程开始执行
while True:
length = len(threading.enumerate()) #查看当前的线程数量
print("当前的线程:%d" % length)
if length <= 1:
break
time.sleep(1)
print("结束时间的时间%s"% ctime())
注意:线程的数量在运行的时候需要写在在循环里面否则退不出来会一直报错的
threading模块能完成多任务的程序开发,怎么让每个线程的封装性更完美呢,这里要是用类的继承来写
列4 通过继承threading.Thread类来写
import threading
import time
class Animal(threading.Thread):
def run(self):
for i in range(3):
msg = self.name + str(i)
print(msg)
time.sleep(1)
print("跑步")
def eat(self):
for i in range(3):
msg = self.name+"--"+str(i)
print(msg)
time.sleep(1)
print("吃饭")
if __name__ == '__main__':
e = Animal() #
e.eat() # 调用吃的线程
e.start() #默认的是执行run方法,因为父类里面有run方法
python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。
说明 : 多线程程序的执行顺序是不确定的,每个线程都有自己的名字
例5多线程的全局变量是共享的
from threading import Thread
import time
num = 100
def fun1():
global num
for i in range(3):
num += i
print("fun1的num是 %d" % num)
def fun2():
global num
for i in range(3):
num += i
print("fun2的num是 %d" % num)
if __name__ == '__main__':
print("num的初始值是%d" % num)
t1 = Thread(target=fun1)
t1.start()
time.sleep(1)
t2 = Thread(target=fun2)
t2.start()
运行的结果
num在运行执行的值是100
fun1的num是 103
fun2的num是 106
结论:可以看出num在多线程的变量里值是共享的
列6 全局变量当作实参来传递给需要的变量
from threading import Thread
import time
def list1(num):
num.append(44)
def list2(num):
time.sleep(1)
print(num)
numList = [11, 22, 33] # 把要添加的值当作参数传递
if __name__ =='__main__':
t1 = Thread(target=list1,args=(numList,))
t1.start()
t2 = Thread(target=list2, args=(numList,))
t2.start()
运行的结果
[11, 22, 33, 44]
在一个进程内的所有线程共享全局变量,能够在不适用其他方式的前提下完成多线程之间的数据共享(这点要比多进程要好)
缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)
列子 7生产消费模式
from queue import Queue
import time
class Producer(threading.Thread):
def run(self):
global queue
count = 0
while True:
if queue.qsize() <1000: # 判断里面是否还有数据
for i in range(100):
count += 1
msg = '生成产品'+str(count)
queue.put(msg)
print(msg)
time.sleep(0.5)
class Counsumer(threading.Thread):
def run(self):
global queue
while True:
if queue.qsize() > 100:
for i in range(3):
msg = self.name + '消费了' + queue.get()
print(msg)
time.sleep(1)
if __name__ == '__main__':
queue = Queue() # 存放了第三方的变量
for i in range(500):
queue.put('初始产品'+str(i))
for i in range(2):
p = Producer()
p.start()
for i in range(5):
c = Counsumer()
c.start()
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦。