多线程基本案例
import time
import threading
def single():
for i in range(5):
print("唱歌")
# time.sleep(1)
def dance():
for i in range(5):
print("跳舞")
# time.sleep(1)
def main():
t1 = threading.Thread(target=single)
t2 = threading.Thread(target=dance)
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
if __name__ == "__main__":
main()
# 获取当前运行的线程数,包括主线程
print(len(threading.enumerate())) # 3
"""
并行:真的多任务
并发:假的多任务 时间片轮转,优先级调度
"""
多线程继承案例
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = self.name + "-----" + str(i) # name属性中保存的是当前线程的名字
print(msg)
if __name__ == '__main__':
t = MyThread()
# 会调用类中的run方法
t.start()
global的使用场景
"""
在函数中对全局变量进行修改的时候,到底是否需要使用global进行说明
要看是否对全局变量的指向进行了修改
如果修改了指向,即让全局变量指向了一个新的地方,那么必须使用global
如果仅仅修改了指向空间中的数据,则不需要global
例如:数字 字符串 元组是不可变的,针对不可变的就是指向会变化,必须添加global的
但是pycharm中在新版本解释器,只要不加global就报错,所以全部加上即可
"""
num = 100
ns = [1, 2]
def t1():
global num
num += 100
def t2():
ns.append(3)
ns += [100]
t1()
t2()
多线程问题
import threading
import time
num = 1
def t1(temp):
global num
for i in range(temp):
num += 1;
def t2(temp):
global num
for i in range(temp):
num += 1;
def main():
# args传递的元组对应就是函数中的temp
t11 = threading.Thread(target=t1, args=(1000000,))
t22 = threading.Thread(target=t2, args=(1000000,))
t11.start()
t22.start()
time.sleep(3)
print(num) #1453092
if __name__ == '__main__':
main()
互斥锁
import threading
import time
num = 1
def t1(temp):
global num
for i in range(temp):
"""
尽量减少加锁的代码,这样结果等效
但是可以两个线程根据cpu竞争执行,不会造成一个for执行完毕全部执行完才执行下一个
t2的for循环,效率更高
"""
# 上锁
mutex.acquire()
num += 1
mutex.release()
def t2(temp):
global num
for i in range(temp):
mutex.acquire()
num += 1
mutex.release()
# 创建一个互斥锁
mutex = threading.Lock()
def main():
# args传递的元组对应就是函数中的temp
t11 = threading.Thread(target=t1, args=(1000000,))
t22 = threading.Thread(target=t2, args=(1000000,))
t11.start()
t22.start()
time.sleep(3)
print(num)
if __name__ == '__main__':
main()
进程
import multiprocessing
"""
进程:所有子进程会开辟单独空间,复制所有代码去单独的空间,和线程相比,更耗费资源
所以变量,属性方法等不共用
主进程不会等待子进程执行完毕才结束
"""
def t1(q):
# 添加数据
for i in range(5):
q.put(i)
def t2(q):
# 取出数据
# waitting_list=[] 下面等效
waitting_list = list()
while True:
data = q.get()
waitting_list.append(data)
# 队列空了就退出
if q.empty(): # q.full() 判断队列是否满了。
break
print(waitting_list)
"""
进程间通信使用消息队列
"""
def main():
# 如果添加参数,例如3这些数字,则代表最多放入3个数据,继续添加时候则需要等待,取出之后才能放进去
q = multiprocessing.Queue()
p1 = multiprocessing.Process(target=t1, args=(q,))
p2 = multiprocessing.Process(target=t2, args=(q,))
p1.start()
p2.start()
if __name__ == '__main__':
main()
进程池
import multiprocessing
import os
def t1(i):
print("执行" + str(i))
print(os.getpid())
def main():
# 代表最大进程数3
p = multiprocessing.Pool(3)
for i in range(0, 10):
# 此处添加了10个任务,但是只有3个进程,当有空闲进程之后,才会继续执行后续任务
p.apply_async(t1, (i,))
# 关闭进程池,关闭之后p不在接收新的请求
p.close()
# 等待所有子进程执行完毕,必须放在close之后,否则主进程的代码执行过快的话,可能无法打印子进程的log就主进程直接退出了
p.join()
if __name__ == '__main__':
main()
迭代器
from collections import Iterable
from collections import Iterator
class ClassMate(object):
def __init__(self):
self.names = list()
self.current_num = 0
def add(self, name):
self.names.append(name)
def __iter__(self):
return self
def __next__(self):
if len(self.names) > self.current_num:
item = self.names[self.current_num]
self.current_num += 1
return item
else:
# 这样的主动抛出异常才会在执行for in 遍历结尾的时候自动停止
raise StopIteration
c = ClassMate()
c.add("张三")
c.add("李四")
# 判断一个对象是否是可迭代对象用如下方法:判断依据是有没有__iter__方法
print("是否是可迭代对象:", isinstance(c, Iterable))
print(iter(c)) # <__main__.ClassMate object at 0x7f5ea1293b38> 调用iter其实就是魔法方法,获取到的返回值就是__iter__的返回值
print("是否是迭代器:", isinstance(c, Iterator))
print(next(c)) # 此时返回是空,但是通过验证可知其实就是__next()__返回值,例如给__next__直接返回20,此处打印就是20
for i in c:
print(i)
"""
总结:
一个可迭代对象不见得是迭代器,但是迭代器肯定是可迭代对象
优势:
数据在需要的时候才生成,节省大量内存
例如:python2中range和xrange
xrange就是返回的可迭代对象
而range直接就是开启好内存的全部数据地址
但是python3中只有range,而且相当于2中的xrange
重点: 此处存储名称的案例内存优势不明显,但是如果是比如动态生成斐波那契数列的代码则优势明显
迭代器不只for循环能接收可迭代对象,list tuple等也可以
"""
生成器(是一种特殊的迭代器)
# 创建生成器的方式一
# 和列表的区别只是一个是[] 一个是(),但是生成器更节省内存
nums = (2 * x for x in range(5))
print(nums) # <generator object <genexpr> at 0x7f32d49c9b48>
# 创建生成器的方式二(常用)
# 函数只要有yield则是一个生成器
def create_num(index):
a, b = 0, 1
current_num = 0
while current_num < index:
yield a
a, b = b, a + b
current_num += 1
return "ok"
items = create_num(10)
print(items) # <generator object create_num at 0x7fc139e47bf8>
print(items.__next__())
print(next(items))
# 迭代方式一
for i in items:
print(i)
# 迭代方式二(该种方式可以获取return返回值)
while True:
try:
print(next(items))
except Exception as ret:
print(ret.value)
break
通过send方式启动生成器
def create_num(index):
a, b = 0, 1
current_num = 0
while current_num < index:
ret = yield a
print(ret)
a, b = b, a + b
current_num += 1
items = create_num(10)
next(items)
# 通过send方式启动生成器,可以传入参数,此时None就是上面的ret
# 但是注意,因为是返回值,所以必须next方式先调用一次
print(items.send(None)) # 1
基于yield实现多任务
import time
def test1():
while True:
print("----1------")
time.sleep(0.1)
yield
def test2():
while True:
print("----2------")
time.sleep(0.1)
yield
def main():
t1 = test1()
t2 = test2()
while True:
next(t1)
next(t2)
if __name__ == '__main__':
main()
基于greenlet实现多任务
# 首先sudo pip3 install greenlet
import time
from greenlet import greenlet
def test1():
while True:
print("----1------")
gr2.switch()
time.sleep(0.1)
def test2():
while True:
print("----2------")
gr1.switch()
time.sleep(0.1)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
def main():
# 切换到gr1中运行
gr1.switch()
if __name__ == '__main__':
main()
# 其实是对yiled进行了封装,启动时候,就是启动某一个,然后进行该函数,该函数会启动切换到其他,实现多任务
基于gevent实现多任务(常用)
# 首先sudo pip3 install gevent
from gevent import monkey
import gevent
# 有耗时操作时需要
monkey.patch_all() # 将程序中用到的耗时操作代码,换为gevnet中自己实现的模块,这样就可以直接使用time.sleep
# 否则的话,所有的耗时操作例如sleep socekt接收等等,都要使用gevent专用的函数,很复杂
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0.5)
# gevent其实是对greenlet的再次封装,此处的gr1就是greenlet对象
# gr1 = gevent.spawn(f, 2)
# gr2 = gevent.spawn(f, 2)
#
#
# def main():
# gr1.join()
# gr2.join()
# 上面注释部分可以使用下面实现,是等效的
def main():
gevent.joinall([
gevent.spawn(f, 2),
gevent.spawn(f, 2)
])
if __name__ == '__main__':
main()
"""
输出:
<Greenlet at 0x7f9f4a4ce848: f(2)> 0
<Greenlet at 0x7f9f4a4ce848: f(2)> 1
<Greenlet at 0x7f9f4a4cec48: f(2)> 0
<Greenlet at 0x7f9f4a4cec48: f(2)> 1
添加延时之后:
<Greenlet at 0x7f05eebbc848: f(2)> 0
<Greenlet at 0x7f05eebbcc48: f(2)> 0
<Greenlet at 0x7f05eebbc848: f(2)> 1
<Greenlet at 0x7f05eebbcc48: f(2)> 1
得出结论:gevent针对不延时的操作不会进行调度,会先执行完所有才执行其他
但是添加gevent.sleep(0.5)之后,有延时操作之后才是真的调度多任务,gevent中睡眠不能使用time.sleep无效
"""