Python(多线程threading模块)
day27
参考:http://www.cnblogs.com/yuanchenqi/articles/5733873.html
CPU像一本书,你不阅读的时候,你室友马上阅读,你准备阅读的时候,你室友记下他当时页码,等下次你不读的时候开始读。
多个线程竞争执行。
进程:A process can have one or many threads.一个进程有多个线程。
一个线程就是一堆指令集合。
线程和进程是同样的东西。
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
import time
import threading begin = time.time()
def foo(n):
print('foo%s'%n)
time.sleep(1) def bar(n):
print('bar%s'%n)
time.sleep(2) # foo()
# bar()
# end = time.time() #并发,两个线程竞争执行
t1 = threading.Thread(target = foo, args =(1,) )
t2 = threading.Thread(target = bar, args =(2,) )
t1.start()
t2.start() t1.join()#t1,t2执行完再往下执行
t2.join()
#t1,t2同时执行
end = time.time() print(end - begin)
并发,两个线程竞争执行
执行结果:
foo1
bar2
2.002244710922241 Process finished with exit code 0
IO密集型任务函数(以上为IO密集型)计算效率会被提高,可用多线程
计算密集型任务函数(以下为计算密集型)改成C语言
import time
import threading
begin = time.time() def add(n):
sum = 0
for i in range(n):
sum += i
print(sum) # add(50000000)
# add(80000000) #并发,两个线程竞争执行
t1 = threading.Thread(target = add, args =(50000000,) )
t2 = threading.Thread(target = add, args =(80000000,) )
t1.start()
t2.start() t1.join()#t1,t2执行完再往下执行
t2.join()
end = time.time() print(end - begin)
计算密集型中用并发计算效率并没有提高。
计算效率并没有提高。
GIL
In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once.
在同一时刻,只能有一个线程。
使之有多个进程就可以解决(如果三个线程无法同时进行,那么把它们分到三个进程里面去,用于解决GIL问题,实现并发)。
线程与进程的区别:
- Threads share the address space of the process that created it; processes have their own address space.
- Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.
- Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.
- New threads are easily created; new processes require duplication of the parent process.
- Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.
- Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.
threading_test.py
import threading
from time import ctime,sleep
import time def music(func):
for i in range(2):
print ("Begin listening to %s. %s" %(func,ctime()))
sleep(1)
print("end listening %s"%ctime()) def move(func):
for i in range(2):
print ("Begin watching at the %s! %s" %(func,ctime()))
sleep(5)
print('end watching %s'%ctime()) threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2) if __name__ == '__main__': for t in threads:#线程加到了列表中
# t.setDaemon(True)
t.start()
# t.join()
# t1.join()
#t2.join()########考虑这三种join位置下的结果?
print ("all over %s" %ctime()) #一共执行10秒
一共只执行10秒,因为是同时执行,看哪个时间长。2*5s
执行结果:
Begin listening to 七里香. Fri Nov 2 16:43:09 2018
all over Fri Nov 2 16:43:09 2018
Begin watching at the 阿甘正传! Fri Nov 2 16:43:09 2018
end listening Fri Nov 2 16:43:10 2018
Begin listening to 七里香. Fri Nov 2 16:43:10 2018
end listening Fri Nov 2 16:43:11 2018
end watching Fri Nov 2 16:43:14 2018
Begin watching at the 阿甘正传! Fri Nov 2 16:43:14 2018
end watching Fri Nov 2 16:43:20 2018 Process finished with exit code 0
join
import threading
from time import ctime,sleep
import time def music(func):
for i in range(2):
print ("Begin listening to %s. %s" %(func,ctime()))
sleep(2)
print("end listening %s"%ctime()) def move(func):
for i in range(2):
print ("Begin watching at the %s! %s" %(func,ctime()))
sleep(3)
print('end watching %s'%ctime()) threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2) if __name__ == '__main__': for t in threads:#线程加到了列表中
# t.setDaemon(True)
t.start()
#t.join() #变成了串行 t1已经执行完了,但是t2阻塞了,其中t为t2
t1.join() #all over在第四秒就会被打印,因为t1四秒执行完,不再阻塞,而t2还在执行
#t2.join()########考虑这三种join位置下的结果?
print ("all over %s" %ctime()) #一共执行6秒
t1.join,t1执行完才能到下一步,所以4秒后才能print ("all over %s" %ctime())
t2.join,t2执行结束才能到下一步,所以6秒后才能print ("all over %s" %ctime())
如果将t.join()放到for循环中,即和串行一样先执行t1,再执行t2。
setDeamon
import threading
from time import ctime,sleep
import time def music(func):
for i in range(2):
print ("Begin listening to %s. %s" %(func,ctime()))
sleep(2)
print("end listening %s"%ctime()) def move(func):
for i in range(2):
print ("Begin watching at the %s! %s" %(func,ctime()))
sleep(3)
print('end watching %s'%ctime()) threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2) if __name__ == '__main__': t2.setDaemon(True)
for t in threads:#线程加到了列表中
#t.setDaemon(True)
t.start() print ("all over %s" %ctime()) #主线程只会等待没设定的子线程t1,t2被设定setDaemon
#t1已经执行完(4s),但是t2还没执行完,和主线程一起退出
32 #主线程只会等待没设定的子线程t1,t2被设定setDaemon
33 #t1已经执行完(4s),但是t2还没执行完,和主线程一起退出 执行结果:
Begin listening to 七里香. Fri Nov 2 17:33:29 2018
Begin watching at the 阿甘正传! Fri Nov 2 17:33:29 2018
all over Fri Nov 2 17:33:29 2018
end listening Fri Nov 2 17:33:31 2018
Begin listening to 七里香. Fri Nov 2 17:33:31 2018
end watching Fri Nov 2 17:33:32 2018
Begin watching at the 阿甘正传! Fri Nov 2 17:33:32 2018
end listening Fri Nov 2 17:33:33 2018 Process finished with exit code 0
4秒就结束。
print属于主线程! 继承式调用
import threading
import time class MyThread(threading.Thread):#继承
def __init__(self, num):
threading.Thread.__init__(self)
self.num = num def run(self): # 定义每个线程要运行的函数 print("running on number:%s" % self.num) time.sleep(3)
if __name__ == '__main__':
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
同步锁
import time
import threading def addNum():
global num #在每个线程中都获取这个全局变量 temp = num time.sleep(0.0001)#在前一次还没执行完,就开始减1
num =temp-1 #对此公共变量进行-1操作 num = 100 #设定一个共享变量
thread_list = []
r = threading.Lock()#同步锁
for i in range(100): t = threading.Thread(target=addNum)
t.start()
thread_list.append(t) for t in thread_list: #等待所有线程执行完毕
t.join() print('final num:', num )#有join所有执行完再输出
执行结果:
final num: 47 Process finished with exit code 0
最终结果不是0的原因:由于有sleep的原因,100个减一操作几乎同时进行,前一次还在sleep没进行减法运算,全局变量就被后一次线程进行减法运算。
正常情况:100-1=99,99-1=98........1-1 = 0。
有sleep:100-1=99(还没减),全局变量100被拿走,进行下一线程的运算100-1=99,造成最后结果不为0;
解决方法:同步锁,使数据运算部分变成了串行。
import time
import threading def addNum():
global num #在每个线程中都获取这个全局变量
#num -= 1 r.acquire()#同步锁,又变成串行
temp = num
#print('--get num:',num )
time.sleep(0.0001)#在前一次还没执行完,就开始减1
num =temp-1 #对此公共变量进行-1操作
r.release()
#只是将以上的部分变成了串行 print('ok')
#将不是数据的部分内容不放到锁中,100个线程同时拿到ok,这部分将不是串行,而是并发 num = 100 #设定一个共享变量
thread_list = []
r = threading.Lock()#同步锁
for i in range(100): t = threading.Thread(target=addNum)
t.start()
thread_list.append(t) for t in thread_list: #等待所有线程执行完毕
t.join() print('final num:', num )#有join所有执
锁中的部分变成了串行,只有运行结束才进入下一线程。
但是锁外面的部分print('ok')还是并发的,100个线程同时拿到ok。
死锁和递归锁
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。
import threading,time class myThread(threading.Thread):
def doA(self):
lockA.acquire()
print(self.name,"gotlockA",time.ctime())
time.sleep(3)#以上部分被A锁住 lockB.acquire()#下面的也锁住
print(self.name,"gotlockB",time.ctime()) lockB.release()#释放后,执行doB
lockA.release() def doB(self):
#在此过程中,第二个线程进入,因为A,B已经被释放
lockB.acquire()#有锁,正常输出,由于第二个进入,所以A(第二个线程),B(第一个线程)几乎同时获取
#但是之后第一个线程想要获取A锁的时候,A锁已经被第二个线程占着,造成死锁.
print(self.name,"gotlockB",time.ctime())
time.sleep(2) lockA.acquire()
print(self.name,"gotlockA",time.ctime())#没有被打印,反而第二个线程的被打印了 lockA.release()
lockB.release() def run(self):
self.doA()
self.doB() if __name__=="__main__": lockA=threading.Lock()#两个锁
lockB=threading.Lock() #lock = threading.RLock()#该锁可以多次获取,多次acquire和release
threads=[]
for i in range(5):#5个线程
threads.append(myThread())
for t in threads:
t.start()
for t in threads:
t.join()#等待线程结束,后面再讲。
执行结果:
Thread-1 gotlockA Sat Nov 3 13:40:48 2018
Thread-1 gotlockB Sat Nov 3 13:40:51 2018
Thread-1 gotlockB Sat Nov 3 13:40:51 2018
Thread-2 gotlockA Sat Nov 3 13:40:51 2018
以上程序卡住不能运行,doA运行完锁A锁B都释放,准备运行doB,休眠2秒后,获取锁A,此时由于线程锁都被释放,可以进入其他线程,如进入线程二,同时也获取锁A,两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
解决方法:
import threading,time class myThread(threading.Thread):
def doA(self):
lock.acquire()
print(self.name,"gotlockA",time.ctime())
time.sleep(3)#以上部分被A锁住 lock.acquire()#下面的也锁住
print(self.name,"gotlockB",time.ctime()) lock.release()#释放后,执行doB
lock.release() def doB(self):
#在此过程中,第二个线程进入,因为A,B已经被释放
lock.acquire()#有锁,正常输出,由于第二个进入,所以A(第二个线程),B(第一个线程)几乎同时获取
#但是之后第一个线程想要获取A锁的时候,A锁已经被第二个线程占着,造成死锁.
print(self.name,"gotlockB",time.ctime())
time.sleep(2) lock.acquire()
print(self.name,"gotlockA",time.ctime())#没有被打印,反而第二个线程的被打印了 lock.release()
lock.release() def run(self):
self.doA()
self.doB() if __name__=="__main__": # lockA=threading.Lock()#两个锁
# lockB=threading.Lock() lock = threading.RLock()#该锁可以多次获取,多次acquire和release
threads=[]
for i in range(5):#5个线程
threads.append(myThread())
for t in threads:
t.start()
for t in threads:
t.join()#等待线程结束,后
lock = threading.RLock(),该锁可以重用,只用lock。不用lockA,lockB。
执行结果:
Thread-1 gotlockA Sat Nov 3 13:48:04 2018
Thread-1 gotlockB Sat Nov 3 13:48:07 2018
Thread-1 gotlockB Sat Nov 3 13:48:07 2018
Thread-1 gotlockA Sat Nov 3 13:48:09 2018
Thread-3 gotlockA Sat Nov 3 13:48:09 2018
Thread-3 gotlockB Sat Nov 3 13:48:12 2018
Thread-4 gotlockA Sat Nov 3 13:48:12 2018
Thread-4 gotlockB Sat Nov 3 13:48:15 2018
Thread-4 gotlockB Sat Nov 3 13:48:15 2018
Thread-4 gotlockA Sat Nov 3 13:48:17 2018
Thread-2 gotlockA Sat Nov 3 13:48:17 2018
Thread-2 gotlockB Sat Nov 3 13:48:20 2018
Thread-2 gotlockB Sat Nov 3 13:48:20 2018
Thread-2 gotlockA Sat Nov 3 13:48:22 2018
Thread-5 gotlockA Sat Nov 3 13:48:22 2018
Thread-5 gotlockB Sat Nov 3 13:48:25 2018
Thread-3 gotlockB Sat Nov 3 13:48:25 2018
Thread-3 gotlockA Sat Nov 3 13:48:27 2018
Thread-5 gotlockB Sat Nov 3 13:48:27 2018
Thread-5 gotlockA Sat Nov 3 13:48:29 2018
信号量(Semaphore)
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)
BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。
import threading,time
class myThread(threading.Thread):
def run(self):
if semaphore.acquire():
print(self.name)
time.sleep(3)
semaphore.release()
if __name__=="__main__":
semaphore=threading.Semaphore(5)
thrs=[]
for i in range(23):
thrs.append(myThread())
for t in thrs:
t.start()
执行结果:
Thread-1
Thread-2
Thread-3
Thread-4
Thread-5
Thread-6
Thread-7
Thread-8
Thread-9
Thread-10
Thread-11
Thread-12
Thread-13
Thread-14
Thread-15
Thread-16
Thread-17
Thread-18
Thread-19
Thread-20
Thread-21
Thread-22
Thread-23 Process finished with exit code 0
一次同时输出五个,最后一次输出三个。
条件变量同步(Condition)
线程间通信的作用
import threading,time
from random import randint
class Producer(threading.Thread):
def run(self):
global L#一屉
while True:
val=randint(0,100)
print('生产者',self.name,":Append"+str(val),L) if lock_con.acquire():#锁 ,与lock_con.acquire()一样
L.append(val)#做包子,从后面加
lock_con.notify()#通知wait,激活wait
lock_con.release()
time.sleep(3)
class Consumer(threading.Thread):
def run(self):
global L
while True:
lock_con.acquire()
if len(L)==0:#没包子
lock_con.wait()#wait阻塞 print('消费者',self.name,":Delete"+str(L[0]),L)
del L[0]#从前面吃
lock_con.release()
time.sleep(0.1) if __name__=="__main__": L=[]
lock_con=threading.Condition()#条件变量的锁
threads=[]
for i in range(5):#启动五个人在做包子,5个线程
threads.append(Producer())
threads.append(Consumer())#
for t in threads:
t.start()
for t in threads:
t.join()
当一屉中有包子的时候,notify激活waiting,添加包子,和吃包子时有线程锁。
同步条件(Event)
import threading,time class Boss(threading.Thread):
def run(self):
print("BOSS:今晚大家都要加班到22:00。")
event.isSet() or event.set()#set()设为true
time.sleep(5)
print("BOSS:<22:00>可以下班了。")
event.isSet() or event.set() class Worker(threading.Thread):
def run(self):
event.wait()#等待老板决定,阻塞
print("Worker:哎……命苦啊!")
#event.clear() # 标志位 False 等老板说可以下班, 设为true
time.sleep(1)
event.clear()#标志位 False 等老板说可以下班, 设为true
event.wait()#等老板说别的 ,设为true后
print("Worker:OhYeah!") #print Oh,Yeah if __name__=="__main__":
event=threading.Event()
threads=[]
for i in range(5):#五个worker
threads.append(Worker())
threads.append(Boss())#一个老板
for t in threads:
t.start()
for t in threads:
t.join()
boss说完后,5个worker马上能有反应。boss输出后,even.set(),标志位变为True,worker中的event.wait()才能停止阻塞。之后还需将标志位设为False,即event.clear()。
再次等待boss说完话后even.set()将标志位变为True,worker再次发言。
执行结果:
BOSS:今晚大家都要加班到22:00。
Worker:哎……命苦啊!
Worker:哎……命苦啊!
Worker:哎……命苦啊!
Worker:哎……命苦啊!
Worker:哎……命苦啊!
BOSS:<22:00>可以下班了。
Worker:OhYeah!
Worker:OhYeah!
Worker:OhYeah!
Worker:OhYeah!
Worker:OhYeah! Process finished with exit code 0
Python(多线程threading模块)的更多相关文章
- 再看python多线程------threading模块
现在把关于多线程的能想到的需要注意的点记录一下: 关于threading模块: 1.关于 传参问题 如果调用的子线程函数需要传参,要在参数后面加一个“,”否则会抛参数异常的错误. 如下: for i ...
- python中threading模块详解(一)
python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...
- python多线程threading.Lock锁用法实例
本文实例讲述了python多线程threading.Lock锁的用法实例,分享给大家供大家参考.具体分析如下: python的锁可以独立提取出来 mutex = threading.Lock() #锁 ...
- Python:多线程threading模块
目录 Thread对象 Lock对象 local对象 Thread对象: 多任务可以由多进程完成,也可以由一个进程内的多线程完成.进程是由至少1个线程组成的. threading模块在较低级的模块 _ ...
- python编程中的并发------多线程threading模块
任务例子:喝水.吃饭动作需要耗时1S 单任务:(耗时20s) for i in range(10): print('a正在喝水') time.sleep(1) print('a正在吃饭') time. ...
- [转]python 多线程threading简单分析
多线程和多进程是什么自行google补脑 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂.所以,这里力图用简单的例子,让你对多线程有个初步的认识. 单线程 在好些年前的 ...
- 多线程threading模块
python的多线程编程 简介 多线程编程技术可以实现代码并行性,优化处理能力,同时功能的更小划分可以使代码的可重用性更好.Python中threading和Queue模块可以用来实现多线程编程. 详 ...
- Python多线程 - threading
目录 1. GIL 2. API 3. 创建子线程 4. 线程同步 4.1. 有了GIL,是否还需要同步? 4.1.1. 死锁 4.1.2. 竞争条件 4.1.3. GIL去哪儿了 4.2. Lock ...
- 学会使用Python的threading模块、掌握并发编程基础
threading模块 Python中提供了threading模块来实现线程并发编程,官方文档如下: 官方文档 添加子线程 实例化Thread类 使用该方式新增子线程任务是比较常见的,也是推荐使用的. ...
随机推荐
- Jmeter将HTTP request报文体中的字符串转换为大写
<awd><client id='${__javaScript("${IndividualID}".toUpperCase())}'><member ...
- msys2 git status显示中文文件名问题
git config [--global] core.quotepath off https://stackoverflow.com/questions/5854967/git-msysgit-acc ...
- 上海第八中学 shader
http://shiba.hpe.cn/jiaoyanzu/wuli/soft/xna.aspx?classId=4
- Pseudo-class和pseudo-element的差别
相同点: Pseudo-class和pseudo-element的语法都是以selector或者selector.class开始的. 不同点: Pseudo-class的操作对象是文档树中已有的元素, ...
- 选择排序(直接排序)java语言实现
class demo { public static void main(String[] args) { int[] arr={1,4,2,6,8,9,0,5,3,2,2,4,4,6,7,8}; f ...
- 2018.10.19 bzoj1057: [ZJOI2007]棋盘制作(悬线法)
传送门 悬线法板题. 如果只求最大矩形面积那么跟玉蟾宫是一道题. 现在要求最大正方形面积. 所以每次更新最大矩形面积时用矩形宽的平方更新一下正方形答案就行了. 代码: #include<bits ...
- systemctl自定义service
应用场景:开启自启动运行脚本/usr/local/manage.sh 一.服务介绍 CentOS 7的服务systemctl脚本存放在:/usr/lib/systemd/,有系统(system)和用户 ...
- 右值引用和std::move函数(c++11)
1.对象移动 1)C++11新标准中的一个最主要的特性就是移动而非拷贝对象的能力 2)优势: 在某些情况下,从旧内存拷贝到新内存是不必要的,此时对对象进行移动而非拷贝可以提升性能 有些类如IO类或un ...
- python面向对象-3类的静态方法和类方法
还是以上次的洗衣机例子: class Washer: company='ZBL' def __init__(self,water=10,scour=2): self._water=water #不想让 ...
- SPSS—回归—二元Logistic回归案例分析
数据分析真不是一门省油的灯,搞的人晕头转向,而且涉及到很多复杂的计算,还是书读少了,小学毕业的我,真是死了不少脑细胞, 学习二元Logistic回归有一段时间了,今天跟大家分享一下学习心得,希望多指教 ...