18.1 引言/动机

18.2 线程和进程

18.2.1 什么是进程(重量级进程)?

计算机程序只不过是磁盘中可执行的,二进制(或其他类型)的数据,他们只有在被读取到内存中,被操作系统调用时才开始他们的生命期,进程是程序的一次执行,每个进程都有自己的地址空间,内存,数据栈以及其他记录其运行轨迹的赋值数据,操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间,进程可以通过fork和spawn操作来完成其他任务,其之间用进程间通讯(IPC)

18.2.2 什么是线程(轻量级进程)?

线程跟进程有些相似,不同的是,所有线程运行在同一个进程中,共享相同运行环境,他们可以想象成是在主进程或"主线程"中并行运行的"迷你进程".

一个进程中的各个线程之间共享同一片数据空间,从而更方便的共享数据和相互通讯,线程一般都是并行执行.

18.3 Python,线程和全局解释器锁

18.3.1 全局解释器锁(GIL)

Python代码的执行由Python虚拟机(也叫解释器主循环)来控制,对虚拟机的访问由卷曲解释器锁(GIL)来控制,正式这个锁能保证同一时刻只有一个线程在运行

在多线程环境中,Python虚拟机按一下方式执行:

1.设置GIL

2.切换到一个线程去运行

3.运行:1.执行数量的字节码指令 或2.线程主动让出控制(调用time.sleep(0))

4.把线程设置为睡眠状态

5.解锁GIL

6.再次重复以上所有步骤

18.3.2 退出线程

当一个线程结束计算,它就退出了,线程可以调用thread.exit()之类的退出函数.或标准的sys.exit()或抛出一个SystemExit异常等,不过你不可以直接杀掉一个线程

主线程应该是一个好的管理者,它了解每个线程都做些什么事,线程都需要什么数据和参数,以及在线程结束的时候,他们提供了什么结果,这样主线程就可以把各个线程的结果组成一个有意义的最后结果.

18.3.3 在Python中使用线程

在解释器里判断线程是否可用,只要导入thread模块后未报错即表示线程可用

>>> import thread

>>>

18.3.4 没有线程支持的情况

例,单线程中运行的循环()

在单线程中顺序执行两个循环,一定要一个循环结束,另一个才能开始,总时间是各个循环运行时间之和

# vi onethr.py

-----------------------------------------

#!/usr/bin/env python

from time import sleep,ctime

def loop0():

print 'start loop 0 at:',ctime()

sleep(4)

print 'loop 0 done at:',ctime()

def loop1():

print 'start loop 1 at:',ctime()

sleep(2)

print 'loop 1 done at:',ctime()

def main():

print 'starting at:',ctime()

loop0()

loop1()

print 'all DONE at:', ctime()

if __name__ == '__main__':

main()

-----------------------------------------

输出:

# python onethr.py

starting at: Wed Dec  4 07:01:26 2013

start loop 0 at: Wed Dec  4 07:01:26 2013

loop 0 done at: Wed Dec  4 07:01:30 2013

start loop 1 at: Wed Dec  4 07:01:30 2013

loop 1 done at: Wed Dec  4 07:01:32 2013

all DONE at: Wed Dec  4 07:01:32 2013

假定loop0()和loop1()例做的不是睡眠,而是各自独立,不相关的运算,各自运算结果到最后将汇总成一个最终的结果

18.3.5 Python的threading模块

Python提供了thread,threading和Queue等多线程编程模块

注:避免使用thread模块

因为使用thread模块里的属性有可能会与threading出现冲突,threading较thread对线程支持更加完善'

18.4 thread模块

thread模块和锁对象

函数描述

thread模块函数

start_new_thread(function,

args, kwargs=None)产生一个新的进程,在新线程中用指定的参数和可选kwargs来调用这个函数

allocate_lock()分配一个LookType类型的锁对象

exit()让线程退出

LockType类型锁对象方法

acquire(wait=None)尝试获得锁对象

locked()如果获取了锁对象返回True,否则返回False

release()释放锁

例,这儿执行的是和onethr.py中一样的循环,不同的是,这次我们使用的是thread模块提供的简单的多线程机制,两个循环

# vi mtsleep1.py

------------------------------

#!/usr/bin/env python

import thread

from time import sleep,ctime

def loop0():

print 'start loop 0 at:', ctime()

sleep(4)

print 'loop 0 done at:',ctime()

def loop1():

print 'start loop 1 at:', ctime()

sleep(2)

print 'loop 1 done at:', ctime()

def main():

print 'starting at:',ctime()

thread.start_new_thread(loop0, ())

thread.start_new_thread(loop1, ())

sleep(6)

print 'all DONE at:', ctime()

if __name__=='__main__':

main()

-------------------------------

这个程序的输出与之前的输出大不相同,之前是运行了6,7秒,现在是4秒

因为睡眠4秒和2秒的代码现在是并发执行的,这样就使得运行时间被缩短了

在这里,我们使用了sleep()函数作为我们的同步机制

# python mtsleep1.py

-----------------------------------------

starting at: Fri Dec 20 23:56:42 2013

start loop 1 at: Fri Dec 20 23:56:42 2013

start loop 0 at: Fri Dec 20 23:56:42 2013

loop 1 done at: Fri Dec 20 23:56:44 2013

loop 0 done at: Fri Dec 20 23:56:46 2013

all DONE at: Fri Dec 20 23:56:48 2013

------------------------------------------

例,通过使用锁来完成任务

这里,使用锁比mtsleep1.py那里在主线程中使用sleep()函数更合理

# vi mtsleep2.py

------------------------------------

#!/usr/bin/env python

import thread

from time import sleep,ctime

loops = [4,2]

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:

while locks[i].locked(): pass

print 'all DONE at:', ctime()

if __name__ == '__main__':

main()

------------------------------------

# python mtsleep2.py

---------------------------

starting at: Sat Dec 21 01:13:06 2013

start loop 1 at: Sat Dec 21 01:13:06 2013

start loop 0 at: Sat Dec 21 01:13:06 2013

loop 1 done at: Sat Dec 21 01:13:08 2013

loop 0 done at: Sat Dec 21 01:13:10 2013

all DONE at: Sat Dec 21 01:13:10 2013

---------------------------

在线程结束时,线程要自己去做解锁操作,最后一个循环只是坐在那一直等,直到两个锁都被解锁为止才继续运行,由于我们顺序检查每一个锁,所以我们可能会要长时间地等待运行时间长且放在前面的线程,当这些线程的锁释放后,后面的锁可能早就释放了,结果主线程只能毫不停歇地完成对后面这些锁的检查

thread模块只是作为演示,在使用多线程程序应该使用更高级别的模块,如threading等

18.5 threading模块

接下来,我们要介绍的是更高级别的threading模块,他不仅提供了Thread类,还提供了各种非常好用的同步机制

这里我们不在提到锁原语,而Thread类也有某种同步机制

threading模块对象

threading模块对象描述

Thread标识一个线程的执行对象

RLock锁原语对象(跟thread模块里的锁对象相同)

Condition条件变量对象能让一个线程停下来,等待其他线程满足了某个条件,如,状态的改变或值的改变

Event通用的条件变量,多个线程可以等待某个事件的发生,在事件发生后,所有的线程都会被激活.

Semaphore为等待锁的线程提供一个类似"等候室"的结构

BoundedSemaphore与Semaphpre类似,只是它不允许超过初始值

Timer与Thread相似,只是,它要等待一段时间后才开始运行.

核心提示:守护线程

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

如果你的主线程要退出的时候,不用等待那些子线程完成,那就设定这些线程的daemon属性

如果你想要等待子线程完成再退出,那就什么都不用做

整个Python会在所有的非守护线程退出后才会结束,即进程中没有非守护线程存在的时候才结束

18.5.1 Thread类

用Thread类,你可以用多种方法来创建线程:

1.创建一个Thread的实例,传给它一个函数

2.创建一个Thread的实例,传给它一个可调用的类对象

3.从Thread派生出一个子类,创建一个这个子类的实例

函数描述

start()开始线程的执行

run()定义线程的功能的函数(一般会北子类重写)

join(timeout=None)程序挂起,直到线程结束:如果给了timeout,则最多阻塞timeout秒

getName()返回线程的名字

setName(name)设置线程的名字

isAlive()布尔标志,标识这个线程是否还在运行中

isDaemon()返回线程的daemon标志

setDaemon(daemonic)把线程的daemon标志设为daemonic

创建一个Thread的实例,传给它一个函数

例,使用thread模块,threading模块的Thread类有一个join()函数,允许主线程等待线程的结束

# vi mtsleep3.py

-------------------------------

#!/usr/bin/env python

import threading

from time import sleep,ctime

loops = [4,2]

def loop(nloop,nsec):

print 'start loop', nloop, 'at:', ctime()

#!/usr/bin/env python

import threading

from time import sleep,ctime

loops = [4,2]

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:

threads[i].start()

for i in nloops:

threads[i].join()

print 'all DONE at:', ctime()

if __name__ == '__main__':

main()

-------------------------------

# python mtsleep3.py

-------------------------------

starting at: Sat Dec 21 12:14:57 2013

start loop 0 at: Sat Dec 21 12:14:57 2013

start loop 1 at: Sat Dec 21 12:14:57 2013

loop 1 done at: Sat Dec 21 12:14:59 2013

loop 0 done at: Sat Dec 21 12:15:01 2013

all DONE at: Sat Dec 21 12:15:01 2013

--------------------------------

所有线程都创建了之后,再一起调用start()函数启动,而不是创建一个启动一个,而且不用再管理一堆锁,只要简单对每个线程调用join()函数就可以

创建一个Thread实例,传给它一个可调用的类对象

此例中,我们传了一个可调用的类的实例,而不是仅传一个函数,相对mtsleep3.py中的方法来说,这样做更具面向对象的概念

# vi mtsleep4.py

------------------------------

#!/usr/bin/env python

import threading

from time import sleep,ctime

loops = [4,2]

class ThreadFunc(object):

def __init__(self,func,args,name=''):

self.name = name

self.func = func

self.args = args

def __call__(self):

apply(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 = threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__))

threads.append(t)

for i in nloops:

threads[i].start()

for i in nloops:

threads[i].join()

print 'all DONE at:', ctime()

if __name__ == '__main__':

main()

------------------------------

# python mtsleep4.py

---------------------------

starting at: Sat Dec 21 18:51:07 2013

start loop 0 at: Sat Dec 21 18:51:07 2013

start loop 1 at: Sat Dec 21 18:51:07 2013

loop 1 done at: Sat Dec 21 18:51:09 2013

loop 0 done at: Sat Dec 21 18:51:11 2013

all DONE at: Sat Dec 21 18:51:11 2013

----------------------------

18.5.4 斐波那契,阶乘和累加和

从Thread派生出一个子类,创建一个这个子类的实例

例,我们现在要子类化Thread类,而不是创建它的实例

# vi mtsleep5.py

-------------------------------

#!/usr/bin/env python

import threading

from time import sleep, time, ctime

loops = [ 4, 2 ]

class MyThread(threading.Thread):

'''

MyThread derives from the threading.Thread baseclass.

Rather than a callable class, the run() method is

automatically invoked when the thread begins execution.

'''

# constructor

def __init__(self, func, args, name=''):

threading.Thread.__init__(self)

self.name = name

self.setFunc(func, args)

def setFunc(self, func, args):

'''

setFunc() is the method that sets the function

to be called as well as its arguments

'''

self.func = func

self.args = args

def getResult(self):

'''

getResult() provides the return value

from the function call

'''

return self.res

def run(self):

'''

save the return value from the function call

into an instance var rather than returning it

'''

# * 1.6 * self.res = self.func(*self.args)

self.res = apply(self.func, self.args)

# loop() is the same as before

def loop(nloop, nsec):

print 'start loop', nloop, 'at:', ctime(time())

sleep(nsec)

print 'loop', nloop, 'done at:', ctime(time())

def main():

# variable setup

print 'starting threads...'

threads = []

nloops = range(len(loops))

# allocate (but do not start) threads

for i in nloops:

t = MyThread(loop, (i, loops[i]), loop.__name__)

threads.append(t)

# start threads

for i in nloops:

threads[i].start()

# wait for all threads to complete

for i in nloops:

threads[i].join()

print 'all DONE'

if __name__ == '__main__':

main()

-------------------------------

# python mtsleep5.py

------------------------------

starting threads...

start loop 0 at: Sat Dec 21 23:44:07 2013

start loop 1 at: Sat Dec 21 23:44:07 2013

loop 1 done at: Sat Dec 21 23:44:09 2013

loop 0 done at: Sat Dec 21 23:44:11 2013

all DONE

-----------------------------

为了让mtsleep5.py中,Thread的子类更为通用,我们把子类单独放在一个模块中,加上一个getResult()函数用以返回函数的运行结果

例,myThread子类化Thread

# vi myThread.py

------------------------------------

#!/usr/bin/env python

import threading

from time import time, ctime

class MyThread(threading.Thread):

def __init__(self, func, args, name=''):

threading.Thread.__init__(self)

self.name = name

self.func = func

self.args = args

def getResult(self):

return self.res

def run(self):

print 'starting', self.name, 'at:', \

ctime()

self.res = apply(self.func, self.args)

print self.name, 'finished at:', \

ctime()

------------------------------------

例,斐波那契,阶乘和累加和

在这个多线程程序中,我们会分别在单线程和多线程环境中,运行三个递归函数

# vi mtfacfib.py

------------------------------

#!/usr/bin/env python

from myThread import MyThread

from time import time, ctime, sleep

def fib(x):

sleep(0.005)

if x < 2: return 1

return (fib(x-2) + fib(x-1))

def fac(x):

sleep(0.1)

if x < 2: return 1

return (x * fac(x-1))

def sum(x):

sleep(0.1)

if x < 2: return 1

return (x + sum(x-1))

funcs = (fib, fac, sum)

n = 12

def main():

nfuncs = range(len(funcs))

print '*** SINGLE THREAD'

for i in nfuncs:

print 'starting', funcs[i].__name__, \

'at:', ctime(time())

print funcs[i](n)

print funcs[i].__name__, 'finished at:', \

ctime(time())

print '\n*** MULTIPLE THREADS'

threads = []

for i in nfuncs:

t = MyThread(funcs[i], (n,),

funcs[i].__name__)

threads.append(t)

for i in nfuncs:

threads[i].start()

for i in nfuncs:

threads[i].join()

print threads[i].getResult()

print 'all DONE at:', ctime(time())

if __name__ == '__main__':

main()

------------------------------

# python mtfacfib.py

-----------------------------------------

*** SINGLE THREAD

starting fib at: Sat Dec 21 23:55:31 2013

233

fib finished at: Sat Dec 21 23:55:34 2013

starting fac at: Sat Dec 21 23:55:34 2013

479001600

fac finished at: Sat Dec 21 23:55:35 2013

starting sum at: Sat Dec 21 23:55:35 2013

78

sum finished at: Sat Dec 21 23:55:37 2013

*** MULTIPLE THREADS

starting fib at: Sat Dec 21 23:55:37 2013

starting fac at: Sat Dec 21 23:55:37 2013

starting sum at: Sat Dec 21 23:55:37 2013

fac finished at: Sat Dec 21 23:55:38 2013

sum finished at: Sat Dec 21 23:55:38 2013

fib finished at: Sat Dec 21 23:55:39 2013

233

479001600

78

all DONE at: Sat Dec 21 23:55:39 2013

------------------------------------------

18.5.5 threading模块中的其他函数

函数描述

activeCount()当前活动的线程对象的数量

currentThread()返回当前线程对象

enumerate()返回当前活动线程的列表

settrace(func)为所有线程设置一个跟踪函数

setprofile(func) 为所有线程设置一个profile函数

18.5.5 生产者-消费者问题和Queue模块

这个实现中使用了Queue对象和随机地生产(和消耗)货物的方式,生产者和消费者相互独立并且并发的运行

# vi prodcons.py

----------------------------------

#!/usr/bin/env python

from random import randint

from time import time, ctime, sleep

from Queue import Queue

from myThread import MyThread

def writeQ(queue):

print 'producing object for Q...',

queue.put('xxx', 1)

print "size now", queue.qsize()

def readQ(queue):

val = queue.get(1)

print 'consumed object from Q... size now', \

queue.qsize()

def writer(queue, loops):

for i in range(loops):

writeQ(queue)

sleep(randint(1, 3))

def reader(queue, loops):

for i in range(loops):

readQ(queue)

sleep(randint(2, 5))

funcs = (writer, reader)

nfuncs = range(len(funcs))

def main():

nloops = randint(2, 5)

q = Queue(32)

threads = []

for i in nfuncs:

t = MyThread(funcs[i], (q, nloops), \

funcs[i].__name__)

threads.append(t)

for i in nfuncs:

threads[i].start()

for i in nfuncs:

threads[i].join()

print 'all DONE'

if __name__ == '__main__':

main()

----------------------------------

# python prodcons.py

-----------------------------------------

starting writer at: Sun Dec 22 00:00:36 2013

producing object for Q... size now 1

starting reader at: Sun Dec 22 00:00:36 2013

consumed object from Q... size now 0

producing object for Q... size now 1

consumed object from Q... size now 0

producing object for Q... size now 1

consumed object from Q... size now 0

writer finished at: Sun Dec 22 00:00:42 2013

reader finished at: Sun Dec 22 00:00:46 2013

all DONE

-------------------------------------------

本例中,一个要完成多项任务的程序,可以考虑没有任务使用一个线程,这样的程序在设计上相对于单线程做所有事的程序来说,更为清晰

18.6 相关模块

模块描述

thread基本的,底级别的线程模块

threading高级别的线程和同步对象

Queue供多线程使用的同步先进先出(FIFO)队列

mutex互斥对象

SocketServer具有线程控制的TCP和UDP管理

本文转自 showerlee 51CTO博客,原文链接:http://blog.51cto.com/showerlee/1347017,如需转载请自行联系原作者

[PYTHON] 核心编程笔记(18.多线程编程)相关推荐

  1. Go语言编程笔记18:软件测试

    Go语言编程笔记18:软件测试 图源:wallpapercave.com 软件测试也是软件开发的重要组成部分,本篇文章将探讨如何使用Go的标准库和第三方库对程序进行测试. testing Go的标准库 ...

  2. 风变编程第18关 编程思维_动态编程变得容易

    风变编程第18关 编程思维 Imagine you have a bag of coins where each coin is of value 5 dollars and you have to ...

  3. python3多线程编程_Python 3多线程编程学习笔记-基础篇

    本文是学习<Python核心编程>的学习笔记,介绍了Python中的全局解释器锁和常用的两个线程模块:thread, threading,并对比他们的优缺点和给出简单的列子. 全局解释器锁 ...

  4. python多线程编程_python之多线程编程

    python 之多线程编程 我们知道 python 中程序一般是从上往下依次执行的,那么即使没有什么联系的两件事也只 能是等一个执行完后再去执行另一个, 这样的就会很浪费时间, 那么有没有办法让两件事 ...

  5. python创建线程函数_Python多线程编程(三):threading.Thread类的重要函数和方法...

    这篇文章主要介绍threading模块中的主类Thread的一些主要方法,实例代码如下: 复制代码 代码如下: ''' Created on 2012-9-7 @author:  walfred @m ...

  6. VB.NET学习笔记:多线程编程

    在<多线程加委托实现等待窗体(loading正在加载界面),运行超时可以取消操作>一文中使用到了多线程编程,在这里做个笔记. 我们继续使用<再谈委托--同步.异步.Lambda 表达 ...

  7. 【cpp学习笔记】多线程编程

    1.前言 在学习利用多线程来实现多模型的目标检测时,接触到了 condition_variable.future等多线程编程知识,感觉自己对这方面的知识还不熟悉,于是便找了一些学习资料进行学习. 2. ...

  8. 太赞了,288页Python核心知识笔记,零基础入门首选

    为什么要学Python? 应用广.易上手,还有庞大的数据控支撑! 这样的一门编程语言,当然便成了众多编程爱好者的首要学习目标. 但是,在实际的学习过程中,我们发现,Python学习并不容易. 为了让大 ...

  9. java并发编程笔记_java并发编程笔记(一)——并发编程简介

    java并发编程笔记(一)--简介 线程不安全的类示例 public class CountExample1 { // 请求总数 public static int clientTotal = 500 ...

最新文章

  1. LeetCode.917-只反转字母(Reverse Only Letters)
  2. csdn在markdown笔记中复制代码格式混乱的解决办法
  3. Java多线程之synchronized(二)
  4. 开始学习3年前的东西——MCMS
  5. 关于子网划分的几个捷径
  6. html 文本框 自动拼接,HTML 中table的结构以及拼接
  7. MySQL 字段默认值该如何设置
  8. Golang 受欢迎的原因:大道至简
  9. window.onload 函数不执行处理
  10. centos7 python3 爬虫登陆邮箱_Centos7搭建Scrapy爬虫环境
  11. Java语言基础--枚举
  12. 王者荣耀交流协会 — Alpha阶段中间产物
  13. C语言中文网C++教程笔记
  14. python3 歌词文件krc转lrc
  15. 什么是EJB?不再神秘!
  16. 操作系统理论:信号量机制与共享资源的并发访问问题
  17. Qt动画入门QPropertyAnimation
  18. AT32 XMC驱动PC卡/CF卡
  19. 手把手教你开发App(HelloWorld)
  20. PIC16F877A单片机 (中断与定时器Timer1)

热门文章

  1. 思科交换机配置试题_(思科配置试题可以略过)
  2. 查看历史操作记录_燕麦课堂丨操作日志管理,为企业数据安全保驾护航
  3. 小冰公司CEO李笛:AI不会江郎才尽,创造力只会持续向上攀升丨MEET2022
  4. AlphaFold2被超越!中国团队刷新全球蛋白质结构预测纪录,大牛彭健创业项目一鸣惊人...
  5. 他用波士顿动力机器狗拉人力车!网友:这是我见过最蒸汽朋克的事情
  6. 一文读懂并发与并行,同步与异步阻塞
  7. [LeetCode]题解(python):140-Word Break II
  8. URAL 1353 Milliard Vasya's Function DP
  9. Verilog篇(三)仿真原理
  10. C#非泛型集合类-ArrayList数组集合类