线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

threading模块:

threading模块主要用于线程相关的操作。

线程方法:

start            线程准备就绪,等待CPU调度

setName      为线程设置名称

getName      获取线程名称

setDaemon   设置为后台线程或前台线程(默认), 如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止。如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止。

join              逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义

run              线程被cpu调度后自动执行线程对象的run方法

调用线程的两种方式:

1、直接调用

import threading
from time import sleep
def mythread(num):#定义每个线程要执行的操作(线程要做的事情)
print 'the thread num is %s' % num
sleep(3) if __name__ == '__main__':
t1 = threading.Thread(target=mythread,args=(1,))#实例化一个线程,执行mythread函数,传递参数1
t2 = threading.Thread(target=mythread,args=(2,))
t1.start()#启动线程1
t2.start()
print t1.getName()#获取线程1的名字
print t2.getName() 执行结果:
the thread num is 1
the thread num is 2
Thread-1
Thread-2

线程1和线程2同时执行完毕,然后sleep(3)。并不是执行完线程1后sleep(3),然后再执行线程2。因此两个线程是并行执行。

2、继承调用

import threading
from time import sleep class mythread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self)#先继承父类中的构造方法(__init__)
self.num = num def run(self):#必须有一个run方法
print 'the thread num is %s ' % self.num
sleep(3) if __name__ == '__main__':
t1 = mythread(1)
t2 = mythread(2) t1.start()
t2.start()
print t1.getName()
print t2.getName()

执行结果同上。

主线程等待子线程:

以上两个例子中,主线程启动了两个子线程,并和两个子线程同时执行。如果让主线程等待子线程执行完毕后再执行,可以如下操作:

import threading
from time import sleep
def mythread(num):
print 'the thread num is %s' % num
sleep(3) if __name__ == '__main__':
t1 = threading.Thread(target=mythread,args=(1,))
t2 = threading.Thread(target=mythread,args=(2,))
t1.start()
t2.start()
print t1.getName()
print t2.getName() t1.join()#主线程等待t1执行完毕
t2.join()#主线程等待t2执行完毕 print '---main---'#两个子线程执行完,等待3秒,主线程执行。

同时启动10个线程,主线程与子线程同时执行:

import threading
from time import sleep
def mythread(num):
print 'the thread num is %s' % num
sleep(3) if __name__ == '__main__':
for i in range(10):
t = threading.Thread(target=mythread,args=(i,))
t.start()
print '---main---'

同时启动10个线程,主线程等待子线程执行完后再执行:

import threading
from time import sleep
def mythread(num):
print 'the thread num is %s' % num
sleep(3) t_list = [] if __name__ == '__main__':
for i in range(10):
t = threading.Thread(target=mythread,args=(i,))
t.start()
t_list.append(t)#将子线程加到列表中
for i in t_list:
t.join()#等待列表中的全部子线程执行完毕,主线程再执行
print '---main---'

守护进程:

setDaemon实例1:

import time
import threading def run(n):
print '%s running,,,' % n
time.sleep(2)
print 'done...' def main():
for i in range(5):
t = threading.Thread(target=run,args=(i,))
t.start()
t.join(1)
print 'start %s' % t.getName() m = threading.Thread(target=main,args=())
m.setDaemon(True)#将主线程设置为Daemon(守护)线程,它退出时,其它子线程会同时退出,不管是否执行完任务
m.start()
m.join(2)
print 'main thread done...' #执行结果:
0 running,,,
start Thread-2
1 running,,,
main thread done...

setDaemon实例2:

import time
import threading def run(n):
print '%s running,,,' % n
time.sleep(2)
print 'done...' def main():
for i in range(5):
t = threading.Thread(target=run,args=(i,))
t.start()
t.join(1)
print 'start %s' % t.getName() m = threading.Thread(target=main,args=())
m.setDaemon(True)
m.start()
#m.join(2)
print 'main thread done...' #执行结果:
main thread done...

GIL(Glogbal Interpreter Lock):

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

在 CPython中,某一时刻只有一个线程在运行。

首先需要明确的一点是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。

线程锁/互斥锁:

一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据。此时,如果多个线程同时要修改同一份数据,那么将会导致线程运算结果不准确。

eg:

import time
import threading def add():
global num
time.sleep(1)#打乱每个线程的顺序
num -=1
print num num = 10
for i in range(10):
t = threading.Thread(target=add,args=())
t.start() #执行结果:
9
8
6
7
5
4
3
2
1

由结果可以看出,多个线程同时操作一份共享数据,导致将结果不可预期,也称“线程不安全”。

要使结果准确,需要确保某一时刻只有一个线程可以对数据进行操作———线程锁。

eg:

import time
import threading def add():
global num
time.sleep(1)#打乱每个线程的顺序
lock.acquire()#添加一个进程锁
num -=1
print num
lock.release()#解除进程锁 lock = threading.Lock()#实例化一个lock
num = 10
for i in range(10):
t = threading.Thread(target=add,args=())
t.start() #运行结果:
9
8
7
6
5
4
3
2
1

递归锁:

在大锁中还要包含小锁:

def run1():
print("grab the first part data")
lock.acquire()
global num
num +=1
lock.release()
return num
def run2():
print("grab the second part data")
lock.acquire()
global num2
num2+=1
lock.release()
return num2
def run3():
lock.acquire()#在运行run1和run2之前加锁,确保执行完run1后接着执行run2,并且在这过程中没有其他的线程来执行run1和run2
res = run1()
print('--------between run1 and run2-----')
res2 = run2()
lock.release()
print(res,res2) if __name__ == '__main__': num,num2 = 0,0
lock = threading.RLock()
for i in range(10):
t = threading.Thread(target=run3)
t.start() while threading.active_count() != 1:
print(threading.active_count())
else:
print('----all threads done---')
print(num,num2)

GIL(全局锁)与线程锁:

既然有GIL了,为什么还要线程锁呢???

GIL是防止底层多个C原生线程(CPython)同一时间修改同一数据,就是说python解释器不能并行的执行代码。线程锁是为了防止多个线程同时修改同一份数据。

多进程:

    p2.start()
print '===main=== #执行结果:
===main===
hello ahaii
hello tom

主进程等待子进程执行完毕,再执行:

import multiprocessing
from time import sleep def say(name):
print 'hello %s' % name
sleep(2)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=say,args=('ahaii',))
p2 = multiprocessing.Process(target=say,args=('tom',))
p1.start()
p2.start()
p1.join()
p2.join()
print '===main===' #执行结果:
hello ahaii
hello tom#执行完后,等待2s,执行主进程。
===main===

进程之间通信

线程之间可以相互访问,而不同进程之间内存不是共享的,不能相互访问。因此,要实现通信,需要通过第三方方法(管道)。

Queue队列

python中,队列是进程之间进行数据交换的主要形式。Queue模块提供了队列的操作。无论多少个线程向队列中放数据或者取数据,Queue同一时刻只允许一个线程在操作,即它自带锁。

Queue.put():向队列中放数据

Queue.get():从队列中取数据

Queue.qsize():查看队列中剩余数据个数

常用方法:

import Queue

#q = Queue.Queue(maxsize=3)#最大长度
#print q.get()
#print q.get(timeout=2)
q = Queue.PriorityQueue(maxsize=3)#优先级队列 q.put((3,[1,2,3]))#第一组数据为优先级,第二组为数据
q.put((1,22))
q.put((2,33))
#q.put(5)#队列设置长度为3,第四次put时,由于队列已满,会发生阻塞。可以设置超时时间避免阻塞,如:q.put(5,timeout=2)
# print q.qsize()
# print q.full()#判断队列是否已满,False or True
# print q.empty()#判断队列是否已空,False or True
print q.get()
print q.get()
print q.get() #执行结果:
(1, 22)
(2, 33)
(3, [1, 2, 3])

默认Queue队列遵循先进先出的原则,如有特殊要求,可以使用优先级队列。在优先级队列中,优先级最小,权重越大,越现被取到。

eg:

from multiprocessing import Process,Queue

def func(q):
q.put([123,'hello']) if __name__ == '__main__':
q = Queue()
p1 = Process(target=func,args=(q,))#子进程执行func函数,向队列中put数据
p1.start()
print q.get()#父进程向队列中get数据
p1.join() #执行结果:
[123, 'hello']

Queue队列在取数据时,时按照数据存放的先后顺序,每次只能存、取一个数据,顺序遵循“先进先出”的原则。

多次存、取数据:

eg:

from multiprocessing import Process,Queue

def func(q):
q.put([123,'hello']) if __name__ == '__main__':
q = Queue()
p1 = Process(target=func,args=(q,))#子进程执行func函数,向队列中put数据
p2 = Process(target=func,args=(q,))#子进程执行func函数,向队列中put数据
p1.start()
p2.start()
print q.get()#父进程向队列中get数据
print q.get()
p1.join()
p2.join() #执行结果:
[123, 'hello']
[123, 'hello']

Managers:

from multiprocessing import Manager,Process

def f(d,l):#传入一个字典和一个列表
d[1] = ''#修改字典中的值
d[''] = 2
d[0.25] = None
l.append(1)#列表追加1
print l if __name__ == '__main__':
with Manager() as manager:#这种写法好处是,manager执行完后自动销毁。
#manager = Manager()
d = manager.dict()#生成一个字典
l = manager.list(range(5))#生成一个列表
p_list = []
for i in range(10):#生成10个进程
p = Process(target=f,args=(d,l))
p.start()
p_list.append(p)
for res in p_list:
res.join()#等待10个线程执行完毕
print d#主进程执行
print l #执行结果:
[0, 1, 2, 3, 4, 1]
[0, 1, 2, 3, 4, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{0.25: None, 1: '', '': 2}
[0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

由结果可以看出,每个子进程都向同一个列表中追加了1。

进程池:

由于进程比较消耗资源,因此当系统中运行多个进程时,需要设定一个限制。进程池就是设定系统中同一时刻,最多有多少个进程执行。

eg:

from  multiprocessing import Process,Pool
import time def Foo(i):
time.sleep(2)
# print 'hello'
return i+100 def Bar(arg):
print('-->exec done:',arg) pool = Pool(3)#限制最多执行5个进程 for i in range(10):
pool.apply_async(func=Foo, args=(i,),callback=Bar)#异步,执行完Foo后,接着执行Bar(回调),
# pool.apply(func=Foo, args=(i,))#同步,每个进程串行 print('end')
pool.close()
pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭

Python之线程&进程的更多相关文章

  1. python之线程进程协成

    线程与进程 什么是线程 线程是进程一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源,但是它可与同属一个线程的 ...

  2. Python(线程进程3)

    四 协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切 ...

  3. Python(线程进程2)

    新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的: 1. 在UNIX中该系统调用是:fork,fork会创建一个与父进程一模一样的副本,二者有相同的存储映像.同样的环境字符 ...

  4. python中线程 进程 协程

    多线程:#线程的并发是利用cpu上下文的切换(是并发,不是并行)#多线程执行的顺序是无序的#多线程共享全局变量#线程是继承在进程里的,没有进程就没有线程#GIL全局解释器锁#只要在进行耗时的IO操作的 ...

  5. Python之线程、进程和协程

    python之线程.进程和协程 目录: 引言 一.线程 1.1 普通的多线程 1.2 自定义线程类 1.3 线程锁 1.3.1 未使用锁 1.3.2 普通锁Lock和RLock 1.3.3 信号量(S ...

  6. python中的进程、线程(threading、multiprocessing、Queue、subprocess)

    Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...

  7. Python之路-python(paramiko,进程和线程的区别,GIL全局解释器锁,线程)

    一.paramiko 二.进程.与线程区别 三.python GIL全局解释器锁 四.线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生 ...

  8. Python学习--17 进程和线程

    线程是最小的执行单元,而进程由至少一个线程组成.如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间. 进程 fork调用 通过fork()系统调用,就可以生成一个子进程 ...

  9. Python 浅析线程(threading模块)和进程(process)

    线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 进程与线程 什么 ...

随机推荐

  1. Leetcode-34-Search Insert Position-(Medium)

    二分法查找需要插入的位置,需要注意两点 1.如果元素不存在,停止的时候start Index刚好是需要插入的位置 2.如果元素存在,需要向前追溯找到非目标元素的起始边界 #!/usr/local/bi ...

  2. Xcode插件推荐

    deriveddata-exterminator: A magic button in Xcode to exterminate the current project's DerivedData d ...

  3. cuda编程(一)

    环境安装和例程运行 显卡主要有两家,ATI.NVIDIA,简称A卡和N卡.随着GPU计算能力的上升,采用GPU并行计算来加速的应用越来越多. Nvidia创立人之一,黄仁勋(Jen-Hsun Huan ...

  4. ios微信支付成功后点击左上角返回不走回调的问题

    最近做微信支付发现ios9以后出现的跳转其他app后左上角有返回xxx功能会影响微信支付回调,情况如图 返回后不走下面的方法 - (BOOL)application:(UIApplication *) ...

  5. HaoZip(好压) 去广告纯净版 4.4

    软件名称: 好压 去广告纯净版 软件语言: 简体中文 授权方式: 免费软件 运行环境: Win8 / Win7 / Vista / WinXP 软件大小: 6.3MB 图片预览: 软件简介: 好压去广 ...

  6. %type的用法

    //%type //如果声明的变量是直接映射到数据库的某一列上,那么就可以使用%type关键字将变量 //锚定到这个列上.这样做有什么好处呢? //比如: //declare v_ename scot ...

  7. mysql的注释

    一直没怎么用过mysql数据库, 今天用mysqldump备份了一下表结构, 记录一下遇到的问题 1. mysqldump默认导出没有事务和存储过程, 如果想导出这些可以用 -E 和 -R[--rou ...

  8. js---疑点代码段解析

    function count() { var arr = []; for (var i=1; i<=3; i++) { console.log("iii---"+i); ar ...

  9. Sipdroid实现SIP(三): 消息接收处理

    I. 注册回调 RegisterAgent类 在TransactionClient Fail/Success的回调中, 调用RegisterAgentListener的Register Fail/Su ...

  10. 关于LeetCode的Largest Rectangle in Histogram的低级解法

    在某篇博客见到的Largest Rectangle in Histogram的题目,感觉蛮好玩的,于是想呀想呀,怎么求解呢? 还是先把题目贴上来吧 题目写的很直观,就是找直方图的最大矩形面积,不知道是 ...