Python并发机制(二)——多线程

Python并发机制(二)——多线程

实例化threading.Thread()

import threading,time

def fn(n):
  print n
  print str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))

def main():
  tpool = []
  for i in range(10):
    t = threading.Thread(target=fn,args=(0,))
    tpool.append(t)

  for t in tpool:
    t.start()

  for t in tpool:
    threading.Thread.join(t)

if __main__ == '__name__':
  main()

运行结果如下:

1
 1
2016-11-22 01:22:29
2016-11-22 01:22:29
11

2016-11-22 01:22:29
2016-11-22 01:22:29
1
2016-11-22 01:22:29
 1
2016-11-22 01:22:29
1
2016-11-22 01:22:29
1
 12016-11-22 01:22:29

 1
2016-11-22 01:22:29
2016-11-22 01:22:29

自定义线程类的实例化

import threading,time

class myThread(threading.Thread):
  def __init__(self,n):
    threading.Thread.__init__(self)
    self.n = n

  def fn(self):
    print self.n
    print str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))

  def run(self):
    self.fn()

def main():
  tpool = []

  for i in range(10):
    tpool.append(myThread(1))

  for t in tpool:
    t.start()

  for t in tpool:
    t.join()

if __name__ == '__main__':
  main()

运行结果如下:

1
2016-11-22 01:46:36
1
 12016-11-22 01:46:36

2016-11-22 01:46:36
 1
2016-11-22 01:46:36
1
 1
2016-11-22 01:46:36
2016-11-22 01:46:36
1
2016-11-22 01:46:36
1
2016-11-22 01:46:36
1
2016-11-22 01:46:36
 1
2016-11-22 01:46:36

使用multiprocesssing.dummy执行多线程任务

#coding=utf-8

import urllib2
import time
from multiprocesssing.dummy import Pool
import threading
from multiprocesssing import Pool as PPool

urls = [
 'http://www.python.org',
 'http://www.python.org/about/',
 'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
 'http://www.python.org/doc/',
 'http://www.python.org/download/',
 'http://www.python.org/getit/',
 'http://www.python.org/community/',
 'https://wiki.python.org/moin/',
 'http://planet.python.org/',
 'https://wiki.python.org/moin/LocalUserGroups',
 'http://www.python.org/psf/',
 'http://docs.python.org/devguide/',
 'http://www.python.org',
 'http://www.python.org/about/',
 'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
 'http://www.python.org/doc/',
 'http://www.python.org/download/',
 'http://www.python.org/getit/',
 'http://www.python.org/community/',
 'https://wiki.python.org/moin/',
 'http://planet.python.org/',
 'https://wiki.python.org/moin/LocalUserGroups',
 'http://www.python.org/psf/',
 'http://docs.python.org/devguide/',
]

#单线程时间
start = time.time()
result = map(urllib2.urlopen,urls)
t1 = time.time() - start
print "单线程时间:",t1

#4线程时间
time.sleep(1)

start = time.time()
pool = Pool(4)
result = pool.map(urllib2.urlopen,urls)
pool.close()
pool.join()
t4 = time.time() - start
print "4线程时间:",t4

#8线程时间
time.sleep(1)

start = time.time()
pool = Pool(12)
result = pool.map(urllib2.urlopen,urls)
pool.close()
pool.join()
t8 = time.time() - start
print "12线程时间:",t8

#threading模块4线程
time.sleep(1)

start = time.time()
t = []
for i in range(4):
    t.append(threading.Thread(target=map,args=(urllib2.urlopen,urls[i*3:i*3+3])))

for th in t:
    th.start()

for th in t:
    th.join()

t = time.time() - start
print "threading模块4线程:",t

#4进程
time.sleep(1)

start = time.time()
pool = PPool(processes=4)
#注意加_async,非阻塞版本
results = pool.map_async(urllib2.urlopen,urls)
pool.close()
pool.join()
t = time.time() - start
print "4进程:",t

运行结果如下:

单线程时间: 23.5617449284
4线程时间: 5.81678104401
12线程时间: 2.70931982994
threading模块4线程: 4.12135195732
4进程: 5.78697896004

注意:受GIL机制(如下)影响的多线程实质上是交替单线程执行,利用multiproceessing. dummy.Pool实例化的线程池能实现并发,类似于进程池,如果将它们分为两类,则第一类更有利于IO密集型程序(如上例偏IO),第二类更有利于CPU密集型程序

python(CPython)全局锁(GIL)机制

首先需要明确的一点是 GIL 并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把 GIL 归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL

因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

多个线程如果共享一个变量,它们都有可能对变量进行修改,但高级语言的一条语句在CPU执行时是若干步,即使一个简单的计算common = common + 1也分为至少两步:
1. 求和common和1并将值转存到一个临时变量
2. 将临时变量值赋给common

由于线程是交替执行的,所以循环次数足够多时可能出现一个线程过程中执行另一个线程的第一步或第二步,如下:

import time,threading

common = 0

def change(n):
  global common
  for i in range(100000):
    common = common + n
    common = common - n

t1 = threading.Thread(target=change,args=(5,))
t2 = threading.Thread(target=change,args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print common

运行结果如下:

261

所以我们运用线程锁(thrading.Lock())

import time,threading

common = 0
lock = threading.Lock()

def change(n):
  global common
  for i in range(100000):
    lock.acquire()
    try:
      common = common + n
      common = common - n
    #一定会释放锁
    finally:
      lock.release()

t1 = threading.Thread(target=change,args=(5,))
t2 = threading.Thread(target=change,args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print common

运行结果为0

GIL解决了线程间数据一致性和状态同步的困难,但无疑大多数情况下它是低效的,但当大家开始抱怨并试图去拆分和去除GIL的时候,却发现大量库代码开发者已经重度依赖GIL而非常难以去除了。所以GIL的现存更多的是历史原因.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值