26. 多线程爬虫

1. 线程间的资源竞争

  • 一个线程写入,另一个线程读取,是可以的,如果两个线程都要写入,就需要想办法了
  • 创建一把锁,默认是没有上锁,是不可能重复的锁,只能创建一次
  • mutex = threading.Lock()

2. 互斥锁和死锁

2.1 互斥锁

  • 当多个线程同时共享数据的时候,需要同步控制
  • 某个线程要先修改共享数据时,要先将其锁定,此时资源的状态为锁定,其他线程不能改变,变成‘非锁定’的时候,其他线程才能再一次锁定资源。互斥锁保证了每次只有一个线程读取和写入,保证了多线程状态下数据的准确性
# 1.创建锁
mutex = threading.Lock()

# 2.锁定
mutex.acquire()

# 3.解锁
mutex.release

2.2 死锁

线程之间共享多个资源的时候,如果两个线程同时占用一部分资源,又互相等待对方的时候,就造成了死锁

import threading
import time

class MyThread1(threading.Thread):
	def run(self):
		# 对mutex A上锁
		mutexA.acquire()
		
		# 对mutex A上锁后,延时1s,等待另一个线程,把B上锁
		print(self.name+'------do1-----up----')
		time.sleep(1)
		
		mutexB.acquire()
		print(self.name+'-----do1-----down-----')
		mutexB.release()
		
		mutexA.release()

class MyThread2(threading.Thread):
	def run(self):
		# 对mutex A上锁
		mutexB.acquire()
		
		# 对mutex A上锁后,延时1s,等待另一个线程,把B上锁
		print(self.name+'------do1-----up----')
		time.sleep(1)
		
		mutexA.acquire()
		print(self.name+'-----do1-----down-----')
		mutexA.release()
		
		mutexB.release()

mutexA = threading.Lock()
mutexB = threading.Lock()

if __name__ == '__main__':
	t1 = MyThread1()
	t2 = MyThread2()
	t1.start()
	t2.start()

2.3 避免死锁

  • 程序设计时要尽量避免,添加超时时间等等

3. Queue线程

  • 访问全局变量,需要经常加锁,把数据存放到队列中,可以用python提供的安全模块queue
  • python中的安全模块queue提供了安全的同步的队列类,包括FIFO(先进先出)队列queue,LIFO(后入先出)队列LifoQueue,这些队列实现了锁原语,能够在线程中直接所用,使队列实现线程间同步
1. 初始化Queue(maxsize):创建一个先入先出队列。
2. empty():判断队列是否为空
3. full():判断队列是否满了
4. get():从队列中取出最后一个数据
5. put():将一个数据放入队列中

4. 线程同步

天猫精灵:小爱同学
小爱同学:在
天猫精灵:现在几点了?
小爱同学:你猜猜现在几点了

5. 生产者和消费者

  • 生产者和消费者是多线程开发中的一种模式,通过这种模式,可以让代码达到高内聚低耦合的目标,线程管理更加方便,程序分工更加明确
  • 生产者的线程用来生产数据,消费者再容器中取出数据进行消费

5.1 Lock版生产者和消费者

import threading
import random
gMoney = 0
gTimes = 0

gLock = threading.Lock()

class Producer(threading.Thread):
	def run(self):
		global gMoney
		global gTimes
		gLock.acquire()
		while True:
			if gTimes >= 10:
				break
			money = random.randint(0, 100)
			gMoney += money
			gTimes += 1
			print("%s生产了%d元钱" % (threading.current_thread().name, money))
		gLock.release()
		
class Customer(threading.Thread):
	def run(self):
		global gMoney
		while True:
			gLock.acquire()
			money = random.randint(0, 100)
			if gMoney >= money:
				gMoney -= money
				print("%s消费了%d元钱" % (threading.current_thread().name, money))
			else:
				if gTimes >= 10:
					gLock.release()
					break
				print("%s想消费%d元钱,但是余额只有%d" % (threading.current_thread().name, money, gMoney))
			gLock.release()

def main():
	for x in range(5):
		th = Producer(name = "生产者%d号" % x)
		th.start()
	for x in range(5):
		th = Customer(name = "消费者%d" % x)
		th.start() 

if __name__ == '__main__':
	main()

5.2 Condition版生产者和消费者

import threading
import random

gMoney = 0
gTimes = 0

gCon = threading.Condition()
class Producer(threading.Thread):
	def run(self):
		global gMoney
		global gTimes

		while True:
			gCon.acquire()
			if gTimes >= 10;
				gCon.release()
				break
			money = random.randint(0, 100)
			gMoney += money
			gTimes += 1
			print("%s生产了%d元钱,剩余了%d元钱" % (threading.current_thread().name, money,gMoney))
			gCon.notify_all()
			gCon.release()

class Customer(threading.Thread):
	def run(self):
		global gMoney
		while True:
			gCon.acquire()
			money = random.randint(0, 100)

			while gMoney < money:
				if gTime >= 10:
					gCon.release()
					return
				print("%s想消费%d元钱,但是余额只有%d元钱" % (threading.current_thread().name, money, gMoney))
				gCon.wait()

			gMoney -= money
			print("%s消费了%d元钱,剩下%d元钱" % (threading.current_thread().name, money, gMoney))
			gCon.release()

def main():
	for x in range(5):
		th = Producer(name = "生产者%d号" % x)
		th.start()
		
	for x in range(5):
		th = Customer(name = "消费者%d号" % x)
		th.start()

if __name__ == '__main__':
	main()			
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值