Python的多任务

扩展:enumerate方法

names = [1, 2, 3]

for temp in names:
	print(temp)

# 通过enumerate可以得到列表的索引
for temp in enumerate(names):
	print(temp)

# 对返回的结果直接进行拆包
for i, temp in enumerate(names):
	print(i, temp)

实现多任务的三种方式:

  1. 线程(根据cpu核数,可能是并行)
  2. 进程(根据cpu核数,可能是并行)
  3. 协程(并发)

线程

子线程创建的时间

import threading
import time


def test():
	for i in range(5):
		print("-------test1------%d" % i)


def main():
	# Thread只是创建了一个对象
	t1 = threading.Thread(target = test)
	# 当调用start时,才会创建一个子线程并执行
	t1.start()

if __name__ == "__main__":
	main()

知识点:

  1. 主线程结束,子线程会立即结束
  2. 多个子线程之间执行,没有先后顺序;如果想要控制顺序,可以通过time.sleep()来延时执行
  3. 子线程是在start方法调用时,才会创建并执行的
  4. 线程必须存在于进程中

创建对象有两种方法:

  1. 直接用threading.Thread(target = 函数名)
  2. 重写写一个新的类,继承threading.Thread,并且在新的类中,必须写run方法(这种方法适用于这个线程中,需要处理多个函数时)
import threading
import time


class NewThread(threading.Thread):
	# 除了run方法,还可以写别的方法
	# 这种方法适用于这个线程中,需要处理多个方法时
	# 在调用start()时,系统会自动调run(),所以这个类中的别的方法,需要在run()中调用。
	def run(self):
		for i in range(5):
			time.sleep(1)
			msg = "I'm " + self.name + " @ " + str(i)
			print(msg)


if __name__ == "__main__":
	t = NewThread()
	t.start()

线程之间数据共享,使用全局变量

import threading
import time


num = 100

def test_o():
	global num
	num += 100
	print("--------test_o--------%d" % num)


def test_t():
	print("--------test_t--------%d" % num)


def main():
	t1 = threading.Thread(target = test_o)
	t2 = threading.Thread(target = test_t)
	t1.start()
	# 通过延时,保证函数执行的先后顺序
	time.sleep(1)
	t2.start()
	print("in main %d" % num)


if __name__ == "__main__":
	main()

threading.Thread(target= 函数名, args = 参数列表)这个args是一个元组

import threading
import time

g_nums = [11, 22, 33]


def test_o(temp):
	temp.append(44)
	print("------test_o------%s" % str(temp))


def test_t(temp):
	print("------test_t------%s" % str(temp))


def main():
	t1 = threading.Thread(target = test_o, args = (g_nums, ))
	t2 = threading.Thread(target = test_t, args = (g_nums, ))
	t1.start()
	time.sleep(1)
	t2.start()
	time.sleep(1)
	print("in main temp=%s" % str(g_nums))

if __name__ == "__main__":
	main()

但是,在线程同时操作全局变量时,有可能会出现资源竞争,计算错误
互斥锁

import threading
import time


g_num = 0
# 创建一个互斥锁,解决资源竞争的问题
mutex = threading.Lock()

def test_o(num):
	global g_num
	for i in range(num):
		# 如果已经上锁,默认会堵塞,直到解锁
		mutex.acquire()
		g_num += 1
		mutex.release()
	print("g_num = %d" % g_num)


def test_t(num):
	global g_num
	for i in range(num):
		mutex.acquire()
		g_num += 1
		mutex.release()
	print("g_num = %d" % g_num)


def main():
	t1 = threading.Thread(target = test_o, args = (100000, ))
	t2 = threading.Thread(target = test_t, args = (100000, ))
	t1.start()
	t2.start()

	time.sleep(5)
	print("g_num = %d" % g_num)


if __name__ == "__main__":
	main()

进程

使用进程实现多任务

import time
import multiprocessing


def test_o():
	for i in range(5):
		print("----test_o-----%d" % i)
		time.sleep(1)


def test_t():
	for i in range(5):
		print("------test_t-------%d" % i)
		time.sleep(1)


def main():
	p1 = multiprocessing.Process(target=test_o)
	p2 = multiprocessing.Process(target=test_t)
	p1.start()
	p2.start()


if __name__ == "__main__":
	main()

多进程之间通过Queue队列来进行通信

import multiprocessing


def down_web(q):
	data = [11, 22, 33, 44]
	for temp in data:
		# 放入数据
		q.put(temp)
	print("数据已全部存入")


def get_data(q):
	writting = list()
	while True:
		# 取出数据
		data = q.get()
		writting.append(data)
		# 判断队列是否为空,通过q.full()可以判断队列是否已满
		if q.empty():
			break
	print(writting)


def main():
	# 1.创建一个队列
	q = multiprocessing.Queue()

	# 2.创建多个进程,将多个队列当作实参传递到里面
	p1 = multiprocessing.Process(target = down_web, args = (q, ))
	p2 = multiprocessing.Process(target = get_data, args = (q, ))
	p1.start()
	p2.start()


if __name__ == "__main__":
	main()

进程池

如果任务量大时,不可能一次性创建非常多的进程,所以创建一个进程池,不停的向进程池中添加任务,执行完的进程会继续执行之后的任务,如果都在执行,则之后的任务就会堵塞
注意:使用进程池创建Queue队列时,需要用Manger对象的Queue方法

from multiprocessing import Pool
import os
import time
import random

def test(num):
	# 获取当前时间
	t_start = time.time()

	# random.random()随机生成一个0-1之间的浮点数
	time.sleep(random.random()*2)
	t_stop = time.time()

	# 通过os.getpid(),来得到进程id
	print("start = %s,pid = %d" % (num, os.getpid()))
	print("time = %0.2f" % (t_stop - t_start))

def main():
	po = Pool(3)
	for i in range(0, 10):
		po.apply_async(test, (i, ))
	# 关闭进程池,不允许新的任务进入
	po.close()
	# 等待所有任务执行完毕
	po.join()



if __name__ == "__main__":
	main()

协程

迭代器

可以节省内存空间,实现循环
迭代器执行原理:

  1. 如果想要一个对象成为可以迭代的对象,类中必须实现__iter__这个方法
  2. 这个方法需要返回一个同时包含__iter__和__next__这两个方法的类(也可以是自己)
  3. 在执行for来进行遍历迭代时,系统会自动执行__iter__这个方法生成一个迭代器,每循环一次,迭代器会自动调用__next__方法一次
  4. 我们需要将第一个类中需要遍历的变量传入__iter__调用的类中,让第二个类中的__next__方法来调用,不断输出遍历的值
  5. 如果遍历结束,需抛出一个异常,raise StopIteration,for循环得到这个异常,会自动结束
    我们使用list()将元组转换成元组,用tuple()将列表转换成元组都用到了迭代器,不断将数据取出,重新建立新的列表进行append()
from collections.abc import Iterable
from collections.abc import Iterator


class ClassMate(object):
	"""docstring for ClassMate"""
	def __init__(self):
		self.names = list()
		self.count = 0


	def add(self, name):
		self.names.append(name)


	def __iter__(self):
		"""如果想要一个对象成为可以迭代的对象,则类中需要实现__iter__这个方法"""
		# 需要返回一个同时拥有iter()和next()这两个方法的类
		return self


	def __next__(self):	
		if self.count < len(self.names):	
			ret = self.names[self.count]
			self.count += 1
			return ret
		else:
			raise StopIteration



classmate = ClassMate()
classmate.add("test1")
classmate.add("test2")
classmate.add("test3")

# isinstance(对象, Iterable) 验证一个对象是否可以迭代
print("classmate 是否可以迭代", isinstance(classmate, Iterable))

classmate_iterator = iter(classmate)
# isinstance(迭代器, Iterator),验证一个返回值是否是迭代器
print("classmate_iterator 是否是一个迭代器", isinstance(classmate_iterator, Iterator))

for temp in classmate:
	print(temp)
生成器

生成器是特殊的迭代器,可以让函数在执行一部分时暂停,随后还可以继续执行
生成器的原理:

  1. 在方法中写入yield时,这个就不再是一个函数了,而是一个生成器模板
  2. 通过接收返回值得到生成器对象
  3. 不停的执行next(生成器对象),可以得到迭代器相同的效果(在执行next()时,yield会将后面的数据直接传入接收的变量中,但是程序不会停止,只是暂停在当前位置,当你继续执行next()时,程序会继续执行),事实上,调用for进行迭代,系统就会自动调用next()
def create_num(all_num):
	a, b = 0, 1
	current_num = 0
	while current_num < all_num:
		# 如果一个函数中有yield函数,那么这个函数就不再是函数了,而是一个生成器模板
		yield a
		a, b  = b, a+b
		current_num += 1
	return "ok....."


# 如果在调用create_num的时候,发现这个函数有yield,那么此时不是调用函数,而是生成生成器对象
obj = create_num(10)

for temp in obj:
	print(temp)

# 或者使用while循环,当出现StopIteration时,就会停止
while True:
	try:
		ret = next(obj)
		print(ret)
	except Exception as ret:
		print(ret.value)
		break
def create_num(all_num):
	a, b = 0, 1
	current_num = 0
	while current_num < all_num:
		# 如果一个函数中有yield函数,那么这个函数就不再是函数了,而是一个生成器模板
		# ret可以接受send()传过来的值
		ret = yield a
		print("ret =========>", ret)
		a, b  = b, a+b
		current_num += 1
		
# 如果在调用create_num的时候,发现这个函数有yield,那么此时不是调用函数,而是生成生成器对象
obj = create_num(10)
# 连续执行next()也可以得到结果
ret = next(obj)
ret = next(obj)
ret = next(obj)

# send()和next()大体作用一样,只不过send()可以进行传值
# send()一般不在第一次使用,会报错,如果非得使用,send(None)
ret = obj.send("hahaha")
print(ret)

通过yield实现多任务
通过利用yield的特性来实现多任务,因为yield拥有可以暂停后继续执行,并且数据不会丢的特性

import time


def task_o():
	while True:
		print("------task_o-------")
		time.sleep(0.1)
		yield


def task_t():
	while True:
		print("-----task_t--------")
		time.sleep(0.1)
		yield


def main():
	# 创建两个生成器
	t1 = task_o()
	t2 = task_t()
	while True:
		# 交替执行,当执行完next(t1)后遇到yield时,就会退出,然后开始执行next(t2),执行完后就会退出,继续执行next(t1),数据也不会清除,实现了多任务
		next(t1)
		next(t2)



if __name__ == "__main__":
	main()

通过协程来实现多任务
通过时间阻塞时,自动切换任务来实现多任务

  3 import gevent
  4 import time
  5 
  6 
  7 def task_o(n):
  8     for i in range(n):
  9         print(gevent.getcurrent(), i)
 10         # time.sleep(0.1)
 11         gevent.sleep(0.5)
 12 
 13 def task_t(n):
 14     for i in range(n):
 15         print(gevent.getcurrent(), i)
 16         # time.sleep(0.1)
 17         gevent.sleep(0.5)
 18 
 19 def task_h(n):
 20     for i in range(n):
 21         print(gevent.getcurrent(), i)
 22         # time.sleep(0.1)
 23         gevent.sleep(0.5)                                                                                                
 24 
 25 
 26 print("-------1-------")
 27 g1 = gevent.spawn(task_o, 10)
 28 print("-------2-------")
 29 g2 = gevent.spawn(task_t, 10)
 30 print("-------3-------")
 31 g3 = gevent.spawn(task_h, 10)
 32 print("-------3-------")
 33 g1.join()
 34 g2.join()
 35 g3.join() 

如果我们的程序之前已经存在许多耗时操作,那么我们可以通过monkey.path_all()来改变所有的耗时操作为gevent()

  3 import gevent
  4 import time
  5 from gevent import monkey
  6 
  7 
  8 # 当程序存在耗时操作时,他会自动将耗时代码改为gevent,实现多任务                                                         
  9 monkey.patch_all()
 10 
 11 
 12 def task_o(n):
 13     for i in range(n):
 14         print(gevent.getcurrent(), i)
 15         time.sleep(0.5)
 16 
 17 
 18 def task_t(n):
 19     for i in range(n):
 20         print(gevent.getcurrent(), i)
 21         time.sleep(0.5)
 22 
 23 
 24 def task_h(n):
 25     for i in range(n):
 26         print(gevent.getcurrent(), i)
 27         time.sleep(0.5)
 28 
 29 
 30 g1 = gevent.spawn(task_o, 10)
 31 g2 = gevent.spawn(task_t, 10)
 32 g3 = gevent.spawn(task_h, 10)
 33 g1.join()
 34 g2.join()
 35 g3.join() 

也可以这样

  3 import gevent
  4 import time
  5 from gevent import monkey
  6 
  7 # 当程序存在耗时操作时,会自动替换为gevent
  8 monkey.patch_all()
  9 
 10 
 11 def task_o(n):
 12     for i in range(n):
 13         print(gevent.getcurrent(), i)
 14         time.sleep(0.5)
 15 
 16 
 17 def task_t(n):
 18     for i in range(n):
 19         print(gevent.getcurrent(), i)
 20         time.sleep(0.5)
 21 
 22 
 23 def task_h(n):
 24     for i in range(n):
 25         print(gevent.getcurrent(), i)
 26         time.sleep(0.5)
 27 
 28 # 将所有要进行操作的方法,添加到列表中,同上面的效果一样,省时省力
 29 gevent.joinall([
 30     gevent.spawn(task_o, 10),
 31     gevent.spawn(task_t, 10),                                                                                            
 32     gevent.spawn(task_h, 10)
 33 ])
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值