好久没有更新博文啦,在家过春节已经变懒了-_-,不过答应你们更完这个python的入门系列,偶仍是会继续努力的!另外祝愿你们新年快乐,事事顺心!java
咱们学习的不少编程语言,好比java,oc等,都会有线程这个概念.线程的用途很是的普遍,给咱们开发中带来了不少的便利.主要用于一些串行或者并行的逻辑处理,好比点击某个按钮的时候,咱们能够经过进度条来控制线程的运行时间,以便于更好的用于用户的交互.python
每一个独立的线程都包含一个程序的运行入口,顺序的执行序列和一个程序运行的出口.线程必须在程序中存在,而不能独立于程序运行!编程
每一个线程都有他本身的一组cpu储存器,称为线程的上下文,该上下文反应了线程上次运行的cpu寄存器的状态.指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程老是在进程获得上下文运行,这些地址都用于标志拥有线程的进程地址空间中的内存.数组
在Python中,主要提供了thread和threading两个线程模块,thread模块提供了最基础的,最低级的线程函数,和一个简单的锁.threading模块是thread模块的封装进阶,提供了多样的线程属性和方法.下面咱们会对该两个模块逐个解析.安全
thread模块经常使用的函数方法:多线程
函数名 | 描述 |
---|---|
start_new_thread(function, args, kwargs=None) | 产生一个新线程,function为线程要运行的函数名,args是函数参数(tuple元组类型),kwargs为可选参数 |
allocate_lock() | 分配一个locktype类型的线程锁对象 |
exit() | 线程退出 |
_count() | 返回线程数量,注意不包含主线程哦,因此在主线程运行该方法返回的是0 |
locked | locktype 锁,返回true为已锁 |
release() | 释放locktype对象锁 |
acquire() | 锁定 |
下面咱们来举个例子:编程语言
import thread,time
def loop1():
print '线程个数-' + str(thread._count())
i=0
try:
while i < 100:
print i
time.sleep(1)
i = i + 1
except Exception as e:
print e
thread.start_new_thread(loop1,())
复制代码
运行上面代码,你会发现loop1方法中的循环打印并无被调用,而是直接返回了一个异常:函数
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
复制代码
这时你可能会一遍又一遍的检查代码,觉得是代码错了(没错,那我的就是我),其实咱们代码自己是没有错误的,是早期python的thread模块一个缺陷(这个缺陷也是致使这个模块被官方不推荐使用的主要缘由):当咱们在主线程中使用start_new_thread建立新的线程的时候,主线程没法得知线程什么时候结束,他不知道要等待多久,致使主线程已经执行完了,子线程却还未完成,因而系统抛出了这个异常.oop
解决这个异常的方法有两种:学习
1.让主线程休眠足够长的时间来等待子线程返回结果:
import thread,time
def loop1():
print '线程个数-' + str(thread._count())
i=0
try:
while i < 100:
print i
time.sleep(1)
i = i + 1
except Exception as e:
print e
thread.start_new_thread(loop1,())
time.sleep(1000) #让主线程休眠1000秒,足够子线程完成
复制代码
2.给线程加锁(早期python线程使用通常处理)
import thread,time
def loop1(lock):
print '线程个数-' + str(thread._count())
i=0
try:
while i < 100:
print i
time.sleep(1)
i = i + 1
except Exception as e:
lock.release()
print e
lock.release() #执行完毕,释放锁
lock=thread.allocate_lock() #获取locktype对象
lock.acquire() #锁定
thread.start_new_thread(loop1,(lock,))
while lock.locked(): #等待线程锁释放
pass
复制代码
以上就是thread模块的经常使用线程用法,咱们能够看出,thread模块提供的线程操做是极其有限的,使用起来很是的不灵活,下面咱们介绍他的同胞模块threading.
threading模块是thread的完善,有一套成熟的线程操做方法,基本能完成咱们所需的全部线程操做
threading经常使用方法:
threading模块建立线程有两种方式:
1.直接经过初始化thread对象建立:
#coding=utf-8
import threading,time
def test():
t = threading.currentThread() # 获取当前子线程对象
print t.getName() # 打印当前子线程名字
i=0
while i<10:
print i
time.sleep(1)
i=i+1
m=threading.Thread(target=test,args=(),name='循环子线程') #初始化一个子线程对象,target是执行的目标函数,args是目标函数的参数,name是子线程的名字
m.start()
t=threading.currentThread() #获取当前线程对象,这里实际上是主线程
print t.getName() #打印当前线程名字,实际上是主线程名字
复制代码
能够看到打印结果:
循环子线程
MainThread
0
1
2
3
4
5
6
7
8
复制代码
2.经过基础thread类来建立
import threading,time
class myThread (threading.Thread): #建立一个自定义线程类mythread,继承Thread
def __init__(self,name):
"""
从新init方法
:param name: 线程名
"""
super(myThread, self).__init__(name=name)
# self.lock=lock
print '线程名'+name
def run(self):
"""
从新run方法,这里面写咱们的逻辑
:return:
"""
i=0
while i<10:
print i
time.sleep(1)
i=i+1
if __name__=='__main__':
t=myThread('mythread')
t.start()
复制代码
输出:
线程名线程
0
1
2
3
4
5
6
7
8
9
复制代码
若是两个线程同时访问同一个数据的时候,可能会出现没法预料的结果,这时候咱们就要用到线程同步的概念.
上面咱们讲到thread模块的时候,已经使用了线程锁的概念,thread对象的Lock和Rlock能够实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些须要每次只容许一个线程操做的数据,能够将其操做放到acquire和release方法之间.
下面咱们来举例说明,咱们须要实现3个线程同时访问一个全局变量,而且改变这个变量:
import threading,time
lock=threading.Lock() #全局的锁对象
temp=0 #咱们要多线程访问的全局属性
class myThread (threading.Thread): #建立一个自定义线程类mythread,继承Thread
def __init__(self,name):
"""
从新init方法
:param name: 线程名
"""
super(myThread, self).__init__(name=name)
# self.lock=lock
print '线程名'+name
def run(self):
"""
从新run方法,这里面写咱们的逻辑
:return:
"""
global temp,lock
i=0
while i<2: #这里循环两次累加全局变量,目的是增长出错的几率
temp=temp+1 #在子线程中实现对全局变量加1
print self.name+'--temp=='+str(temp)
i=i+1
if __name__=='__main__':
t1=myThread('线程1')
t2=myThread('线程2')
t3=myThread('线程3')
#建立三个线程去执行访问
t1.start()
t2.start()
t3.start()
复制代码
执行结果(因为程序运行很快,你多运行几回就可能会出现如下结果): 咱们能够发现,线程1和线程2同时访问到了变量,致使打印出现对等状况
线程名线程1
线程名线程2
线程名线程3
线程1--temp==1线程2--temp==2
线程1--temp==3
线程2--temp==4
线程3--temp==5
线程3--temp==6
复制代码
import threading,time
lock=threading.Lock() #全局的锁对象
temp=0 #咱们要多线程访问的全局属性
class myThread (threading.Thread): #建立一个自定义线程类mythread,继承Thread
def __init__(self,name):
"""
从新init方法
:param name: 线程名
"""
super(myThread, self).__init__(name=name)
# self.lock=lock
print '线程名'+name
def run(self):
"""
从新run方法,这里面写咱们的逻辑
:return:
"""
global temp,lock
if lock.acquire(): #这里线程进来访问变量的时候,锁定变量
i = 0
while i < 2: # 这里循环两次累加全局变量,目的是增长出错的几率
temp = temp + 1 # 在子线程中实现对全局变量加1
print self.name + '--temp==' + str(temp)
i = i + 1
lock.release() #访问完毕,释放锁让另外的线程访问
if __name__=='__main__':
t1=myThread('线程1')
t2=myThread('线程2')
t3=myThread('线程3')
#建立三个线程去执行访问
t1.start()
t2.start()
t3.start()
复制代码
运行结果(无论运行多少次,都不会出现同时访问的状况):
线程名线程1
线程名线程2
线程名线程3
线程1--temp==1
线程1--temp==2
线程2--temp==3
线程2--temp==4
线程3--temp==5
线程3--temp==6
复制代码
线程同步不少地方都会用到,好比抢票,抽奖,咱们须要对一些资源进行锁定,以防止多线程访问的时候出现不可预知的状况.
python中的队列用到了Queue模块,该模块提供了同步的,安全的对序列,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue.这些队列都实现了锁原语,可以在多线程中直接使用。可使用队列来实现线程间的通讯
Queue模块中的经常使用方法:
例子:
tags=['one','tow','three','four','five','six']
q=Queue.LifoQueue() #先入先出队列
for t in tags:
q.put(t) #将数组数据加入队列
for i in range(6):
print q.get() #取出操做能够放在不一样的线程中,不会出现同步的问题
复制代码
结果:
six
five
four
three
tow
one
复制代码
这章的多线程就到这里了,咱们主要讲述了他的基本用法,更多的用法咱们能够在之后的开发过程当中,根据本身逻辑去设计.