python并发编程之多线程(一)

本文介绍了Python中的全局解释器锁(GIL)、Semaphore和锁在多线程编程中的应用。由于GIL的存在,Python的多线程在CPU密集型任务中无法实现真正的并行,但在IO密集型任务中可以提高并发效率。通过Semaphore限制资源访问量,确保线程安全。此外,Lock(互斥锁)用于保护共享资源,避免数据不一致。
摘要由CSDN通过智能技术生成

编程的乐趣在于让程序越来越快,这里将给大家介绍一个种加快程序运行的的编程方式——多线程

 

1 著名的全局解释锁(GIL)

说起python并发编程,就不得不说著名的全局解释锁(GIL)了。有兴趣的同学可以我查找一下相关的资料了解一下GIL,在这里大家只要知道一点,因为GIL的存在, 对于任何Python程序,不管有多少的处理器,任何时候都总是只有一个线程在执行。
下面先看一个例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on Mon Jun  5 16:12:14 2017

@author: 80002419
"""
import threading
import time

def cost(fun):##定义一个装饰器,用来计算函数运行的时间
    def wrapper(*args,**kargs):
        before_tm = time.time()
        fun(*args,**kargs);
        after_tm = time.time()
        fun.__doc__
        print("{0} cost:{1}".format(fun.__name__,after_tm-before_tm))
    return wrapper

def fibs1(n):
    if (n == 1):
        return 0
    elif(n == 2):
        return 1
    else:
        return fibs1(n-2)+fibs1(n-1)
    
def fibs2(n):
    list = []
    if (n == 1):
        list = [0]
        return list[-1] 
    elif(n == 2):
        list = [0,1]
        return list[-1] 
    else:
        list = [0,1]
        for i in range(2,n):
            list.append(list[i-1]+list[i-2])
        return list[-1]    
 
@cost
def nothread():
    fibs1(35) 
    fibs1(35)


#@cost
#def nothread1():
#    print(fibs2(40))
#    print(fibs2(40))

@cost
#使用多线程的程序
def inthread():
    threads = []
    for i in range(2):
        t = threading.Thread(target = fibs1,args =(35,))
        t.start()
        threads.append(t)
    for t in  threads:
        t.join()

@cost    
def inthread1():
    for i in range(2):
        t = threading.Thread(target=fibs1, args=(35,))
        t.start()
    main_thread = threading.currentThread()
    for t in threading.enumerate():
        if t is main_thread:
            continue
        t.join()

nothread()
inthread()
打印结果:
nothread cost:5.41788887978
inthread cost:14.6728241444
 
上面的例子对比了一个cpu密集型程序分别使用多线程和不使用多线程的情况,我们可以得到结论: 对于cpu密集型任务,使用多线程反而会减慢程序的运行。
这是为什么呢?
 因为GIL在任何时候,仅仅允许一个单一的线程能够获取Python对象或者C API。每100个字节的Python指令解释器将重新获取锁,这(潜在的)阻塞了I/O操作
同时  GIL是必须的,这是Python设计的问题:Python解释器是非线程安全的。这意味着当从线程内尝试安全的访问Python对象的时候将有一个全局的强制锁。当然python 多线程也有可以加快程序运行的时候:当我们开发和网络通信或者数据输入/输出相关的程序,比如网络爬虫、文本处理等等。这时候由于网络情况和I/O的性能的限制,Python解释器会等待读写数据的函数调用返回,这个时候就可以利用多线程库提高并发效率了。这类任务我们称为IO(密集型任务)
 

2  Semaphore类 ——python对象访问量的控制

在多线程编程中,为了防止不同的线程同时对一个公用的资源(比如全部变量)进行修改,需要进行同时访问的数量(通常是1)。信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。
下面来看一个例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on Mon Jun  5 16:12:14 2017

@author: 80002419
"""
from threading import Thread, Semaphore 
import time,threading

sema = Semaphore(3) # 定义一个信号量为3的Semaphone对象,

#定义一个测试函数作为,被访问的对象
def test(tid):
	with sema:
		print("信号量:{0}".format(tid))
		time.sleep(0.5)
		print('release!')
	
thds = []
for i in range(6):
	t = Thread(target = test,args=(i,))
	t.start()
	thds.append(t)
	
for t in thds:
	t.join()
打印结果:
信号量:2
信号量:1
信号量:0
release!
信号量:0
release!
信号量:0
release!
信号量:0
release!
release!
release!
 
结果分析 :
当信号量sema信号为0时,此时不再不允许进程对test 再进行访问了,release 之后才能再继续生成亲的进程对其访问, Semaphore类的作用就是限制公共资源的访问量
 
3 锁——lock 与 Rlock
先看一段代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on Mon Jun  5 16:12:14 2017

@author: 80002419
"""
from threading import Thread, Lock
import time,threading

value = 0;

def changeValue():
	global value
	new = value + 1
	time.sleep(0.001)#让其它进程可以切换进来
	value = new

thds = []
for _ in range(100):
	t = Thread(target = changeValue)
	t.start()
	thds.append(t)

for t in thds:
	t.join()

print(value)
打印结果:
18
结果分析: 多个线程同时访问 value 变量,经过cpu存储后,再写回内存,如果进程A,进程B,在访问value时 值为0 ,进程A经过函数处理后,value = 1 ,再写回内存,value =1 ,进程B 执行完成后,会刷新value 但是写回的value 还是1, 所以并不能保护执行了100次changeValue后,结果为100
 
那么我们怎么能保证执行的结果就是100呢:再看一段代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on Mon Jun  5 16:12:14 2017

@author: 80002419
"""
from threading import Thread, Lock
import time,threading

value = 0

lock = Lock()

def changeValue():
	global value
	with lock:
		new = value + 1
		time.sleep(0.001)#让其它进程可以切换进来
		value = new
		
thds = []

for _ in range(100):
	t = Thread(target = changeValue)
	t.start()
	thds.append(t)
for t in thds:
	t.join()
print(value)

打印结果:
100
Lock也可以叫做互斥锁,其实相当于信号量为1,上面例子lock 也就可以用 sema = Semaphore()代替
 
 
 
 
 
 
 
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

crystalnsd

万水千山总是情,支持一下行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值