多线程
一、线程是什么?
1、线程是进程中包含的执行单元
二、它有什么特点?
1、1个进程可包含多个线程
2、1次只能执行1个线程
3、线程锁(资源竞争问题)
二、主线程和子线程的执行关系
1、主线程会等待子线程结束之后在结束
2、join() 等待子线程结束之后,主线程继续执行
3、setDaemon() 守护线程,不会等待子线程结束
import time
import threading
def demo1():
for i in range(5):
print('hello everybody')
time.sleep(1)
if __name__ == '__main__':
t = threading.Thread(target=demo1)
t.setDaemon(True) # 守护进程
t.start()
t.join() # 等待子线程执行结束之后我在执行
print(1)
# 主线程会等待子线程结束之后在结束
创建多线程
一、通过函数创建多线程
使用threading模块当中的一个Thread类,有一个参数target参数,需要我们传递一个函数对象。一般这个函数是用来实现 线程的逻辑
import threading
import time
def fun():
print('我是第%d个子线程'%i)
if __name__ == '__main__':
for i in range(5): # # 从0开始到4 一共5次 最后一个输出的是:我是第4个子线程
t = threading.Thread(target=fun)
time.sleep(1)
t.start()
2、通过类
自定义一个类吗,需要继承父类 threading.Thread 并重新run()方法 实现线程的逻辑
class F(threading.Thread):
def fun1(self):
for i in range(1, 6): # 从1开始到6 一共5次 最后一个输出的是:我是第5个子线程
time.sleep(1)
print('我是第%d个子线程' % i)
if __name__ == '__main__':
a = F()
a.fun1()
查看线程的数量
一、使用enumerate()
import threading
import time
threading.enumerate()
def demo1():
for i in range(5):
print('demo1--%d'%i)
time.sleep(1)
def demo2():
for i in range(10):
print('demo2--%d' % i)
time.sleep(1)
def main():
t1 = threading.Thread(target=demo1,name='demo1')
t2 = threading.Thread(target=demo2,name='demo2')
t1.start()
t2.start()
while True:
print(threading.enumerate()) # 查看线程数量
if len(threading.enumerate()) <= 1: # 当线程数小于等于1
break
time.sleep(1)
if __name__ == '__main__':
main()
线程间的资源竞争
import threading
import time
# 线程的优点: 效率快 缺点: 线程比较多的时候会出现的问题 1、数据不完整 2、报BUG json()数据 需要控制爬取的频率
# 创建一个线程锁 咋上锁?看源码
# lock = threading.Lock() # 不可重复的锁 只能上一次
lock = threading.RLock() # 如果非要多次上锁就使用RLock() 方法
num = 0
def demo1(nums):
global num
# 上锁
lock.acquire()
lock.acquire()
for i in range(nums): # 100 0 - 99 100
num += 1
# 解锁
lock.release()
lock.release()
print('demo1-num-%d' % num)
def demo2(nums):
global num
lock.acquire()
for i in range(nums):
num += 1
lock.release()
print('demo2-num-%d' % num)
# 因为线程之间的资源竞争关系当数据大到一定程度时到的结果会出现错误所以这里我们需要使用线程锁来解决
def main():
t1 = threading.Thread(target=demo1,args=(1000000,)) # 结果为demo1-num-1000000 正确
t2 = threading.Thread(target=demo2,args=(1000000,)) # 结果为demo2-num-1650015 是错误的正确的应该是demo2-num-2000000
t1.start()
# time.sleep(3)
# t1.join()
t2.start()
# t2.join()
time.sleep(2)
print('main-num-%d' % num) # 结果为main-num-1477872是错误的正确结果应该是main-num-2000000
if __name__ == '__main__':
main()
线程锁
# 线程锁
import threading
import time
# 线程的优点: 效率快 缺点: 线程比较多的时候会出现的问题 1、数据不完整 2、报BUG json()数据 需要控制爬取的频率
# 创建一个线程锁 咋上锁?看源码
# lock = threading.Lock() # 不可重复的锁 只能上一次
lock = threading.RLock() # 如果非要多次上锁就使用RLock() 方法
num = 0
def demo1(nums):
global num
# 上锁
lock.acquire()
lock.acquire()
for i in range(nums): # 100 0 - 99 100
num += 1
# 解锁
lock.release()
lock.release()
print('demo1-num-%d' % num)
线程队列Queue
一、队列的特点 先进先出
1、empty() 判断队列是否为空
2、full() 判断队列是否满了
3、get() 从队列当中取出数据
4、put() 将一个数据添加到队列当中
from queue import Queue
'''
爬虫
empty() 判断队列是否为空
full() 判断队列是否满了
get() 从队列当中取出数据
put() 将一个数据添加到队列当中
'''
a = Queue(3)
# empty() 判断队列是否为空 True代表是空的 False 代表队列不是空的
# print(a.empty()) # 结果为True
# 结果为True l() 判断队列是否满了 False代表不是满的 True 代表队列满了
# print(a.full()) # 结果为False
# put() 将一个数据添加到队列当中
a.put(1)
a.put(2)
a.put(3)
# a.put(4) # 如果超过线程列队 程序就会阻塞
# print('*'*80)
# print(a.empty()) # 结果为False
# print(a.full()) # 结果为 True 代表队列满了
# a.put(4,timeout=2)
# a.put_nowait(4)
# get() 从队列当中取出数据
# print(a.get()) # 结果为 1
# print(a.get()) # 结果为 2
# print(a.get()) # 结果为 3
# print(a.get()) # 超过线程就提取不出数据 并阻塞程序