一.线程
1.多线程是无顺序的
2.len(threading.enumerate())) 输出线程数
import threading,time
def saysorry():
print("i'm sorry!")
#time.sleep(3)
print("Are you ok?")
if __name__ == "__main__" :
for i in range(5):
t=threading.Thread(target=saysorry)
t.start()
#输出线程数
print(len(threading.enumerate()))
3.类调用线程序
import threading,time
#类里面继承线程序
class wang(threading.Thread):
#下面的t.start()会自动调用类里面的类方法
def run(self):
print(123)
def main():
t=wang()
#类多线程序,只会调用run方法。可以在run里面调用类的其它方法
t.start()
if __name__ == "__main__" :
main()
4.函数内要改全局变量,要不要加global的问题总结。
在一个函数中对全局变量进行修改的时候,到底是否需要使用global进行说明要看是否对全局变量的执行指向进行了修改,如果进行了修改需要加gloabl,如果没有进行修改此时不用必须使用global。
num = 10
def wa():
#不加这一条会报错
global num
num += 10
print(num)
wa()
print(num)
5.threading.Tread 传参数args必须是数组
多线程之间共享全局变量。(一个线程抓数据,一个线程处理数据,一个线程发送数据)
import threading,time
dachui=[1,2]
tie=5
def ha(temp,ze):
temp.append(3)
print("ha %s"%str(temp))
def he(temp,ze):
time.sleep(1)
print("he %s %d"%(str(temp),ze))
def main():
t1=threading.Thread(target=ha,args=(dachui,tie))
t2=threading.Thread(target=he,args=(dachui,tie))
t1.start()
t2.start()
time.sleep(1)
print("main %s"%str(dachui))
if __name__ == "__main__" :
main()
6.多线程共享全局变量同时修改数据时会产生资源竞争导致数据结果不正确
举例:同时都加1执行一百万次。电脑中把+1拆分成了多个步骤,这个过程中有时间断,再拿数据的时候可能就不对了。下面这个程序输出的结果数据是不对的。
import threading,time
tie=0
def ha(temp):
global tie
for i in range(temp):
tie += 1
def he(temp):
global tie
for i in range(temp):
tie += 1
def main():
t1=threading.Thread(target=ha,args=(100000,))
t2=threading.Thread(target=he,args=(100000,))
t1.start()
t2.start()
time.sleep(1)
print(tie)
if __name__ == "__main__" :
main()
7.多线程使用互斥锁解决资源紧张问题
上锁的范围应该保证最小化
#创建锁
mutex=threading.Lock()
#锁定
mutex.acquire()
#释放
mutex.release()
还拿加100万举例
import threading,time
tie=0
def ha(temp):
global tie
#上锁,如果之前没被上锁,则此时上锁成功。如果已经被上锁了,则阻塞在这里,直到被解锁
#这个上锁的范围太大了。这个锁的意思锁加完这一百万后才解锁
mutex.acquire()
for i in range(temp):
tie += 1
mutex.release()
def he(temp):
global tie
for i in range(temp):
#上锁范围应该保证最小化
mutex.acquire()
tie += 1
mutex.release()
#创建一个互斥锁,默认是没有上锁的
mutex=threading.Lock()
def main():
t1=threading.Thread(target=ha,args=(1000000,))
t2=threading.Thread(target=he,args=(1000000,))
t1.start()
t2.start()
time.sleep(1)
print(tie)
if __name__ == "__main__" :
main()
8.死锁。(解决办法,添加超时时间)
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
尽管死锁很少发生,但一旦发生就会造成应用的停止响应。下面看一个死锁的例子
import threading
import time
class MyThread1(threading.Thread):
def run(self):
# 对mutexA上锁
mutexA.acquire()
# mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
print(self.name+'----do1---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
mutexB.acquire()
print(self.name+'----do1---down----')
mutexB.release()
# 对mutexA解锁
mutexA.release()
class MyThread2(threading.Thread):
def run(self):
# 对mutexB上锁
mutexB.acquire()
# mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
print(self.name+'----do2---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
mutexA.acquire()
print(self.name+'----do2---down----')
mutexA.release()
# 对mutexB解锁
mutexB.release()
mutexA = threading.Lock()
mutexB = threading.Lock()
if __name__ == '__main__':
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
二.进程
1.多个进程(写时拷贝),有不变的东西时可共享。比如:父子进程共享一份代码,没有复制多份,改变的时候会拷贝。
2.多进程互相交流可以用socket,文件,队列。队列是在内存中开辟的空间,先进先出,效率比较高。redis可以做多电脑之间的消队列。
import multiprocessing
def downdata(q1):
#模拟从下载数据
data=[1,2,3,4]
q1.put(data)
def analysis_data(q1):
#模拟处理数据
da=q1.get()
print(da*2)
def main():
q1=multiprocessing.Queue(1)
p1=multiprocessing.Process(target=downdata,args=(q1,))
p2=multiprocessing.Process(target=analysis_data,args=(q1,))
p1.start()
p2.start()
if __name__ == "__main__" :
main()
3.进程池
比如拷贝文件,不知到有多少个的时候,可以考虑用进程池。
进程池创建后,不会马上创建,到有任务的时候才会去创建进程池。
1.通过进程池创建的子进程,主进程不会等待其执行完。可通.join()让主进程等待。join要放到池close的后面。
2.通过multiprocessing.Process创建的子进程,主进程会等待其执行完后自己才会结束。
三.协程
C和C++没有协程的概念
想让自己定义的类能用迭代的方法,需要在类里用__iter__,要不然用不了。
1.迭代器
代码1
from collections import Iterable
from collections import Iterator
import time
class wang():
def __init__(self):
self.name=list()
def add(self,name):
self.name.append(name)
#有iter方法则这个类就是可迭代的类了,但是不一定能迭代成功
def __iter__(self):
return wa(self.name)
#有iter和next方法的类,可以创建出迭代器
class wa():
def __init__(self,name):
self.name=name
self.num=0
def __iter__(self):
pass
def __next__(self):
if self.num<len(self.name):
print(self.name[self.num])
self.num+=1
else:
raise StopIteration
w=wang()
w.add("laowang")
w.add("dachui")
#判断是不是一个可迭代的对象
#print(isinstance(w,Iterable))
# wwwa=iter(w)
#判断是不是一个迭代器
# print(isinstance(wwwa,Iterator))
# print(next(wwwa))
# print(next(wwwa))
#for命令可以自动去迭代,而不用手动去创建迭代器
for i in w:
print(i)
time.sleep(1)
代码2(升级版)
from collections import Iterable
from collections import Iterator
import time
class wang():
def __init__(self):
self.name=list()
self.num=0
def add(self,name):
self.name.append(name)
#iter返回自己的话用一个类就可以创建出一个迭代器了。
def __iter__(self):
return self
def __next__(self):
if self.num<len(self.name):
print(self.name[self.num])
self.num+=1
else:
raise StopIteration
w=wang()
w.add("laowang")
w.add("dachui")
for i in w:
print(i)
time.sleep(1)
2.greenlet(用的也比较少,用gevent比较多)
需要先用pip安装
from greenlet import greenlet
import time
def test1():
while True:
print "---A--"
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print "---B--"
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
#切换到gr1中运行
gr1.switch()
3.gevent
gevent需要先用pip安装
greenlet是用yield封装的,gevent是拿greenlet封装的,用gevent最方便
gevent遇到延时就切换,不遇到延时不切换。延时是time.sleep还不行,必须要是gevent.sleep()才可以。
包括socket的阻塞,也要切换成gevent的专用参数才行。解决办法给gevent打补丁。
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0.5)
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f1, 5)
g3 = gevent.spawn(f2, 5)
g1.join()
g2.join()
g3.join()
给gevent打延时补丁(monkey.patch_all)
在程序前面加上这两行就可以了
import gevent
import time
#下面这两行就是给gevent打补丁
from gevent import monkey
monkey.patch_all()
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
#gevent.sleep(0.5)
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
#下面也可以一句命令搞定,但是没有join会没有等待。调用spawn时候已经开始执行了,join会等待主线程
#gevent.spawn(f,5)
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f1, 5)
g3 = gevent.spawn(f2, 5)
g1.join()
g2.join()
g3.join()
time.sleep(10)
如果有100个协程需要运行上面的方法太麻烦,可以用gevent.joinall来操作,如下
from gevent import monkey
import gevent
import random
import time
# 有耗时操作时需要
monkey.patch_all() # 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块
def coroutine_work(coroutine_name):
for i in range(10):
print(coroutine_name, i)
time.sleep(random.random())
gevent.joinall([
gevent.spawn(coroutine_work, "work1"),
gevent.spawn(coroutine_work, "work2")
])
4.全局解释器锁GIL锁
C语言的python解释器,多线程会只会有一个线程在执行。多线程和协程适合多IO类的操作,不适合计算密集型类的操作。计算密集型可以用多进程。