本文希望达到的目标:

  1. 多线程的基本认识
  2. 多线程编程的模块和类的使用
  3. Cpython的全局解释器锁GIL

一、多线程的基本认识

多线程编程的目的:并行处理子任务,大幅度地提升整个任务的效率。

线程就是运行在进程上下文的逻辑流。每个线程都有自己的线程上下文,包含唯一的线程ID(就当前所属进程而言),栈,栈指针,程序计数器,通用寄存器和条件码等。

同一个进程中的线程,共享相同的运行环境,共享同一片数据空间,所以线程间的通讯笔进程间的通信更简单,但是这样的共享是会有危险的,如果多线程共同访问同一数据,因为访问顺序的不同,可能会导致结果不一致。

二、多线程编程的模块和类的使用

为了更好说明多线程的优点以及多个标准库使用的差异性,以模拟“一个程序完成2个独立任务时间总和”为例子。

0、单进程单线程运行两个独立的任务:顺序执行,完成第一个任务后,再完成第二个任务。总时间是各个循环 运行时间之和,实际两个任务是完全独立的,如果并行执行,是可以减少运行时间的。

import thread
from time import sleep,ctime def loop0():
print 'start loop0','at:',ctime()
sleep(4)
print 'loop0','done at:',ctime() def loop1():
print 'start loop1','at:',ctime()
sleep(3)
print 'loop1','done at:',ctime() def main():
print 'starting at:',ctime()
loop0()
loop1()
print 'all done at:',ctime() if __name__=='__main__':
main()

1、thread模块

python提供了两个标准库用于多线程编程,thread模块提供了基本的线程和锁的支持,而 threading 提供了更高级别,功能更强的线程管理的功能。一般都建议使用threading模块,毕竟功能更强大,更好管理。

thread模块和对象:(官网:https://docs.python.org/2/library/thread.html)

使用多线程编程,创建两个线程同时执行两个独立的任务,需要考虑,主线程执行时间和子线程执行时间的关系,如果单纯的创建线程去运行这2个任务,主线程执行完成时间必然比子线程快,子线程未运行完,主线程就已经退出了,在thread模块使用锁对象lock来管理,为每个线程创建一个锁对象,在线程执行完成后释放锁,而主线程判断所有的锁都释放后才能结束,进程间的通讯机制就这样简单的建立起来。

import thread
from time import sleep,ctime loops =[,] def loop(nloop,nsec,lock):
print 'start loop',nloop,'at:',ctime()
sleep(nsec)
print 'loop',nloop,'done at:',ctime()
lock.release() def main():
print 'starting at:',ctime()
locks = []
nloops = range(len(loops)) for i in nloops:
lock = thread.allocate_lock()
lock.acquire()
locks.append(lock) for i in nloops:
thread.start_new_thread(loop,(i,loops[i],locks[i])) for i in nloops:
print 'check lock'
while locks[i].locked():
pass print 'all done at:',ctime() if __name__=='__main__':
main()

运行时间为4s,单进程耗时7s,运行时间有减少。为什么不在创建锁的循环里创建线程呢?有以下几个原因:(1) 我 们想到实现线程的同步,所以要让“所有的马同时冲出栅栏”。(2) 获取锁要花一些时间,如果线程退出得“太快”,可能会导致还没有获得锁,线程就已经结束了的情况。

注意:

A:子线程开始:创建对象调用start_new_thread函数时,该函数不是在主线程里运行, 而是产生一个新的线程来运行这个函数。一旦调用该函数,子线程已经开始运行。

B:子线程退出:它不支持守护线程。当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。

2、threading模块 :创建一个 Thread 的实例,传给它一个函数

它不仅提供了 Thread 类,还提供了各 种非常好用的同步机制。

threading模块和对象:(官网:https://docs.python.org/2/library/threading.html)

import threading
from time import sleep,ctime loops =[,] def loop(nloop,nsec):
print 'start loop',nloop,'at:',ctime()
sleep(nsec)
print 'loop',nloop,'done at:',ctime() def main():
print 'starting at:',ctime()
threads = []
nloops = range(len(loops)) for i in nloops:
t = threading.Thread(target=loop,args=(i,loops[i]))
threads.append(t) for i in nloops:
print 'thread',i,'start'
threads[i].start() for i in nloops:
print 'thread',i,'join'
threads[i].join() print 'all done at:',ctime() if __name__=='__main__':
main()

注意:

A、子线程开始:调用start函数。所有的线程都创建了之后,再一起调用 start()函数启动,而不是创建一个启动一个。而且, 不用再管理一堆锁(分配锁,获得锁,释放锁,检查锁的状态等)

B、子线程结束:可以控制子线程和主线程结束的顺序,调用join(timeout=None) 程序挂起,直到线程结束;

C、守护线程一般是一个等待客户请求的服务器, 如果没有客户提出请求,它就在那等着。如果设定一个线程为守护线程,就表示这个线程 是不重要的,在进程退出的时候,不用等待这个线程退出。

3、thread类 :Thread 派生出一个子类,创建一个这个子类的实例

import threading
from time import sleep,ctime loops =(,) class MyThread(threading.Thread):
def __init__(self,func,args,name=''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args def run(self):
self.func(*self.args) def loop(nloop,nsec):
print 'start loop',nloop,'at:',ctime()
sleep(nsec)
print 'loop',nloop,'done at:',ctime() def main():
print 'starting at:',ctime()
threads = []
nloops = range(len(loops)) for i in nloops:
t = MyThread(loop,(i,loops[i]),loop.__name__)
threads.append(t) for i in nloops:
print 'thread',i,'start'
threads[i].start()
for i in nloops:
print 'thread',i,'join'
threads[i].join() print 'all done at:',ctime() if __name__=='__main__':
main()

4、threading模块中的thread类部分源码解析

thread模块提供了一系列基础函数,其实不是不能用,书本上写着的是不建议使用,但是如果用于底层开发是可以的。threading模块与之相比,最大的不同就是,threading模块中的thread类的属性特别多,

包含了对多线程的各自管理上的纬度属性,所以特别方便使用,实际上threading模块就是在thread模块上开发的,做了进一步的集成化和封装以便于用户更轻便的管理。

A :threadding模块有引用thread模块:

try:
import thread
except ImportError:
del _sys.modules[__name__]
raise

B: thread类的初始化函数部分截图如下:初始化的过程,其实就是多线程的属性的初始化的过程。把其中需要的资源,入参,thread管理的各自对象都初始化。

def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, verbose=None): assert group is None, "group argument must be None for now"
_Verbose.__init__(self, verbose)
if kwargs is None:
kwargs = {}
self.__target = target
self.__name = str(name or _newname())
self.__args = args
self.__kwargs = kwargs
self.__daemonic = self._set_daemon()
self.__ident = None
self.__started = Event()
self.__stopped = False
self.__block = Condition(Lock())
self.__initialized = True
# sys.stderr is not stored in the class like
# sys.exc_info since it can be changed between instances
self.__stderr = _sys.stderr

C:thread类的start函数,看到调用底层的_start_new_thread函数,就明白了,为啥thread类是调用start函数来启动线程,还调用了self.__started.wait(),__started对象实际是_Condition类的实例,这是一个对

线程锁管理的实例,调用这个类的wait方法就是在获取一把锁。

 def start(self):
if not self.__initialized:
raise RuntimeError("thread.__init__() not called")
if self.__started.is_set():
raise RuntimeError("threads can only be started once")
if __debug__:
self._note("%s.start(): starting thread", self)
with _active_limbo_lock:
_limbo[self] = self
try:
_start_new_thread(self.__bootstrap, ())
except Exception:
with _active_limbo_lock:
del _limbo[self]
raise
self.__started.wait()
 def wait(self, timeout=None):
if not self._is_owned():
raise RuntimeError("cannot wait on un-acquired lock")
waiter = _allocate_lock()
waiter.acquire()
self.__waiters.append(waiter)
saved_state = self._release_save()
try: # restore state no matter what (e.g., KeyboardInterrupt)
if timeout is None:
waiter.acquire()
if __debug__:
self._note("%s.wait(): got it", self)
else:
# Balancing act: We can't afford a pure busy loop, so we
# have to sleep; but if we sleep the whole timeout time,
# we'll be unresponsive. The scheme here sleeps very
# little at first, longer as time goes on, but never longer
# than times per second (or the timeout time remaining).
endtime = _time() + timeout
delay = 0.0005 # us -> initial delay of ms
while True:
gotit = waiter.acquire()
if gotit:
break
remaining = endtime - _time()
if remaining <= :
break
delay = min(delay * , remaining, .)
_sleep(delay)
if not gotit:
if __debug__:
self._note("%s.wait(%s): timed out", self, timeout)
try:
self.__waiters.remove(waiter)
except ValueError:
pass
else:
if __debug__:
self._note("%s.wait(%s): got it", self, timeout)
finally:
self._acquire_restore(saved_state)

D:而调用join方法,实际也是调用_Condition类的实例,判断当前锁的状态,在线程运行完毕后,释放锁。

    def join(self, timeout=None):
if not self.__initialized:
raise RuntimeError("Thread.__init__() not called")
if not self.__started.is_set():
raise RuntimeError("cannot join thread before it is started")
if self is current_thread():
raise RuntimeError("cannot join current thread") if __debug__:
if not self.__stopped:
self._note("%s.join(): waiting until thread stops", self)
self.__block.acquire()
try:
if timeout is None:
while not self.__stopped:
self.__block.wait()
if __debug__:
self._note("%s.join(): thread stopped", self)
else:
deadline = _time() + timeout
while not self.__stopped:
delay = deadline - _time()
if delay <= :
if __debug__:
self._note("%s.join(): timed out", self)
break
self.__block.wait(delay)
else:
if __debug__:
self._note("%s.join(): thread stopped", self)
finally:
self.__block.release()

三、Cpython的全局解释器锁GIL

推荐一篇更全面介绍的博客:https://www.cnblogs.com/frchen/p/5740606.html

GIL全称 Global Interpreter Lock,GIL 并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。实际现在一般使用的解析器都是基于CPython的,如果是Jpython(基于java),可能就不存在 这个问题。像单 CPU 的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在 CPU 中运行。 在CPython 解释器中可以“运行” 多个线程,但在任意时刻,只有一个线程在解释器中运行。而对 Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

对所有面向 I/O 的(会调用内建的操作系统 C 代码的)程序来说,GIL 会在这个 I/O 调用之前被释放,以允许其它的线程在这个线程等待 I/O 的时候运行。如果某线程并未使用很多 I/O 操作, 它会在自己的时间片内一直占用处理器(和 GIL)。也就是说,I/O 密集型的 Python 程序比计算密集型的程序更能充分利用多线程环境的好处。

简单的总结下就是:Python的多线程在多核CPU上,只对于IO密集型计算产生积极效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降。

python多线程学习一的更多相关文章

  1. python多线程学习(一)

    python多线程.多进程 初探 原先刚学Java的时候,多线程也学了几天,后来一直没用到.然后接触python的多线程的时候,貌似看到一句"python多线程很鸡肋",于是乎直接 ...

  2. python多线程学习记录

    1.多线程的创建 import threading t = t.theading.Thread(target, args--) t.SetDeamon(True)//设置为守护进程 t.start() ...

  3. python 多线程学习小记

    python对于thread的管理中有两个函数:join和setDaemon setDaemon:如果在程序中将子线程设置为守护线程,则该子线程会在主线程结束时自动退出,设置方式为thread.set ...

  4. python多线程学习二

    本文希望达到的目标: 多线程同步原语:互斥锁 多线程队列queue 线程池threadpool 一.多线程同步原语:互斥锁 在多线程代码中,总有一些特定的函数或者代码块不应该被多个线程同时执行,通常包 ...

  5. Python多线程学习

    一.Python中的线程使用: Python中使用线程有两种方式:函数或者用类来包装线程对象. 1.  函数式:调用thread模块中的start_new_thread()函数来产生新线程.如下例: ...

  6. python 多线程学习

    多线程(multithreaded,MT),是指从软件或者硬件上实现多个线程并发执行的技术 什么是进程? 计算机程序只不过是磁盘中可执行的二进制(或其他类型)的数据.它们只有在被读取到内存中,被操作系 ...

  7. Python 多线程学习(转)

    转自:http://www.cnblogs.com/slider/archive/2012/06/20/2556256.html 引言 对于 Python 来说,并不缺少并发选项,其标准库中包括了对线 ...

  8. Python多线程学习资料1

    一.Python中的线程使用: Python中使用线程有两种方式:函数或者用类来包装线程对象. 1.  函数式:调用thread模块中的start_new_thread()函数来产生新线程.如下例: ...

  9. Python多线程学习笔记

    Python中与多线程相关的模块有 thread, threading 和 Queue等,thread 和threading模块允许程序员创建和管理线程.thread模块提供了基本的线程和锁的支持,而 ...

  10. 《转》Python多线程学习

    原地址:http://www.cnblogs.com/tqsummer/archive/2011/01/25/1944771.html 一.Python中的线程使用: Python中使用线程有两种方式 ...

随机推荐

  1. Vue:在vue-cli中使用Bootstrap

    一.安装jQuery Bootstrap需要依赖jQuery,所以引用Bootstrap之前要先引用jQuery,使用下面的命令引用jQuery: npm install jquery --save ...

  2. 【FM】算法

    https://www.cnblogs.com/AndyJee/p/7879765.html

  3. 如何通过ajax来获取返回值

    preface 今天使用ajax向后端取数据的时候,按照正常的写法,死活return不了值,所以看了下ajax的帮助手册,发现了这个参数async:false,这个参数是说async表示不是异步执行了 ...

  4. VS2013-2017 舒服的字体设置和背景色

    使用字体:Fixedsys Excelsior 3.01 1.如果没有安装字体的话,首先下载字体:http://www.fixedsysexcelsior.com/ 2.安装字体:控制面板 -> ...

  5. ansible运维工具(一)

    运维工具介绍 OS Provisioning: PXE, Cobbler(repository, distritution,profile) PXE: dhcp, tftp, (http, ftp) ...

  6. H5常用技巧

    工作每天都是接触移动的,特将平时工作中常用的技巧整理总结. 一.@support断定浏览器支持情况定义不同样式 @1像素边框 @supports (-webkit-backdrop-filter:bl ...

  7. 怎么才能使服务器Nginx(或者Apache)支持字体文件

    为了在前端正确地显示字体,浏览器必须使用正确的http header来接受字体文件.如果服务器没有设置要求的头信息,那么有些浏览器就会在控制台报错或者直接不能显示. 可能你的服务器已经配置好了,你无须 ...

  8. 前端自动化构建工具webpack (二)之css和插件加载总结

    1.  webpack只识别js文件,其他文件都需要转换成js文件.所有文件都是模块; 2. css解析      css需要css-loader  --->style-loader ----- ...

  9. 微信小程序开发笔记01

    微信小程序开发的优势 1,不用安装,即开即用,用完就走.省流量,省安装时间,不占用桌面: 2,体验上虽然没法完全媲美原生APP,但综合考虑还是更优: 3,对于小程序拥有者来说,开发成本更低,他们可以更 ...

  10. php 腾讯地图和百度地图的相互转换

    关于百度和腾讯地图坐标转换的问题,网上大多给的是JS实现的,而JS又是异步实现的,无法返回值,在有些应用场景下不好使用,这里我从腾讯论坛上找到了一个PHP转换的方法,贴出来分享给大家.经测试十分有效! ...