Python_12-线程编程
1.1 Python中的线程使用
1.1.1 函数式
1.2 创建threading.Thread的子类来包装一个线程对象
1.2.1 threading.Thread类的使用
1.3 线程优先级队列(Queue)
1.4 thread对象中的一些方法
1.4.1 join方法
1.4.2 setDaemon()方法
1.4.3 isAlive方法
1.4.4 线程名
1.5 线程同步
1.5.1 简单的线程同步
1.5.2 线程同步
1.5.3 使用条件变量保持线程同步
1.5.4 使用队列保持线程同步
1.1 Python中的线程使用
Python中使用线程有两种方式:函数或者用类来包装线程对象。
多线程类似于同时执行多个不同程序
优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
- 程序的运行速度可能加快
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程在执行过程中与进程还是有区别的。
l 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。
l 线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
l 每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。
l 线程可以被抢占(中断)。
l 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。
1.1.1 函数式
调用thread模块中的start_new_thread()函数来产生新线程。如下例:
import time
import thread
def timer(no, interval):
cnt = 0
while cnt<10:
print 'Thread:(%d) Time:%s/n'%(no, time.ctime())
time.sleep(interval)
cnt+=1
thread.exit_thread() def test(): #Use thread.start_new_thread() to create 2 new threads
thread.start_new_thread(timer, (1,1))
thread.start_new_thread(timer, (2,2)) if __name__=='__main__':
test()
上面的例子定义了一个线程函数timer,它打印出10条时间记录后退出,每次打印的间隔由interval参数决定。thread.start_new_thread(function, args[, kwargs])的第一个参数是线程函数(本例中的timer方法),第二个参数是传递给线程函数的参数,它必须是tuple类型,kwargs是可选参数。
线程的结束可以等待线程自然结束,也可以在线程函数中调用thread.exit()或thread.exit_thread()方法。
1.2 创建threading.Thread的子类来包装一个线程对象
如下例:
import threading
import time
class timer(threading.Thread): #The timer class is derived from the class threading.Thread
def __init__(self, num, interval):
threading.Thread.__init__(self)
self.thread_num = num
self.interval = interval
self.thread_stop = False def run(self): #Overwrite run() method, put what you want the thread do here
while not self.thread_stop:
print( 'Thread Object(%d), Time:%s/n' %(self.thread_num, time.ctime()) )
time.sleep(self.interval)
def stop(self):
self.thread_stop = True def test():
thread1 = timer(1, 1)
thread2 = timer(2, 2)
thread1.start()
thread2.start()
time.sleep(10)
thread1.stop()
thread2.stop()
return if __name__ == '__main__':
test()
该方法创建自己的线程类,必要时重写threading.Thread类的方法,线程的控制可以由自己定制。
1.2.1 threading.Thread类的使用
1. 在自己的线程类的__init__里调用threading.Thread.__init__(self, name = threadname)
Threadname为线程的名字
2. run(),通常需要重写,编写代码实现做需要的功能。
3. getName(),获得线程对象名称
4. setName(),设置线程对象名称
5. start(),启动线程
6. jion([timeout]),等待另一线程结束后再运行。
7. setDaemon(bool),设置子线程是否随主线程一起结束,必须在start()之前调用。默认为False。
8. isDaemon(),判断线程是否随主线程一起结束。
9. isAlive(),检查线程是否在运行中。
此外threading模块本身也提供了很多方法和其他的类,可以帮助我们更好的使用和管理线程。
1.3 线程优先级队列(Queue)
Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。
Queue模块中的常用方法:
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作
实例:
#coding=utf-8
import queue
import threading
import time exitFlag = 0 class myThread (threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print( "Starting " + self.name)
process_data(self.name, self.q)
print( "Exiting " + self.name) def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print ("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1) threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1 # 创建新线程
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1 # 填充队列
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release() # 等待队列清空
while not workQueue.empty():
pass # 通知线程是时候退出
exitFlag = 1 # 等待所有线程完成
for t in threads:
t.join()
print ("Exiting Main Thread")
1.4 thread对象中的一些方法
以前说过多线程,用到threading模块中的Thread对象
start是重载了Thread对象中的run方法,其实作用还是,当执行这个start方法的时候,将运行run方法。
1.4.1 join方法
如果一个线程或者一个函数在执行过程中要调用另外一个线程,并且待到其完成以后才能接着执行,那么在调用这个线程时可以使用被调用线程的join方法。
join([timeout])
里面的参数时可选的,代表线程运行的最大时间,即如果超过这个时间,不管这个线程有没有执行完毕,主线程或函数都会接着执行的。
看个例子:
#coding=utf-8
import threading
import time
class MyThread(threading.Thread):
def __init__(self,id):
threading.Thread.__init__(self)
self.id=id
def run(self):
x=0
time.sleep(20)
print self.id >>> def func():
t.start()
for i in range(5):
print i >>> t=MyThread(2)
>>> func()
0
1
2
3
4
>>> 2
可以看到,虽然在func里面线程已经运行,但是函数并没有等线程运行结束在执行,而是先把func执行完毕,打印0到4,然后等sleep(20),20秒结束后,这个MyThread(2),传进去的2才打印出。
>>> def func():
t.start()
t.join()
for i in range(5):
print i >>> t=MyThread(3)
>>> func()
3
0
1
2
3
4
而这个呢,是当t.start()运行开始计时,20秒后,打印出id是3,然后func才接着运行,打印出0到4.
1.4.2 setDaemon()方法
这个方法基本和join是相反的。当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法啦,
下面的例子就是设置子线程随主线程的结束而结束:
import threading
import time class myThread(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
time.sleep(5) print self.getName() def fun1():
t1.start()
print 'fun1 done' def fun2():
t2.start()
print 'fun2 done' t1=myThread('t1')
t2=myThread('t2')
t2.setDaemon(True)
fun1()
fun2()
上面这个例子,按照我们设想的输出时:
fun1 done
fun2 done
t1
但是实际上我们在交互模式,主线程只有在python退出时终止,所以结果t2也是被打印出来啦。
1.4.3 isAlive方法
当线程创建以后,可以使用Thread对象的isAlive方法查看线程是否运行。
>>> import threading
>>> import time
>>> class myThread(threading.Thread):
def __init__(self,id):
threading.Thread.__init__(self)
self.id=id
def run(self):
time.sleep(5)
print self.id >>> t=myThread(1)
>>> def func():
t.start()
print t.isAlive() >>> func()
True
>>> 1
1.4.4 线程名
当线程创建后可以设置线程名来区分不同的线程,以便对线程进行控制。线程名可以在类的初始化函数中定义,也可以使用Thread对象的setName方法设置。下面是不同的方法来设置线程名。
>>> import threading
>>> class mythread(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
print self.getName() >>>
>>> t1=mythread('t1')
>>> t1.getName()
't1'
>>> t1.setName('T')
>>> t1.getName()
'T'
>>> t2=mythread('t2')
>>> t2.start()
t2
>>> >>> t2.getName()
't2'
>>> t2.setName('TT')
>>> t2.getName()
'TT'
1.5 线程同步
1.5.1 简单的线程同步
使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。如下:
import threading
import time
class mythread(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
global x
lock.acquire()
for i in range(3):
x=x+1
time.sleep(2)
print x
lock.release()
lock=threading.RLock()
t1=[]
for i in range(10):
t=mythread(str(i))
t1.append(t)
x=0
for i in t1:
i.start()
运行结果如下:
>>> 3
6
9
12
15
18
21
24
27
30
而如果我们把acquire()和release()去掉,结果就不同了:
30303030303030303030
这是因为每个线程执行后在打印出x之前都要休眠2秒钟,所以在这个过程中,每个线程都被执行了,所以等到休眠结束,打印出的X的值自然就是经过多次运算以后的X的值了。
而第一次,我们把全局变量X放到了acquire()和release()之间,python解释器每次会只允许一个线程对x进行操作,只有这个线程结束对其操作并且休眠结束打印出来以后,才允许下一个线程对x操作,所以输出的X是每次递增的,而且用时间也是比较长的。
1.5.2 线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
l 使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法.
l 对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。如下:
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。
考虑这样一种情况: 一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。
那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。
经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
实例:
#coding=utf-8
import threading
import time class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("Starting " + self.name)
# 获得锁,成功获得锁定后返回True
# 可选的timeout参数不填时将一直阻塞直到获得锁定
# 否则超时后将返回False
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 释放锁
threadLock.release() def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1 threadLock = threading.Lock()
threads = [] # 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2) # 开启新线程
thread1.start()
thread2.start() # 添加线程到线程列表
threads.append(thread1)
threads.append(thread2) # 等待所有线程完成
for t in threads:
t.join()
print( "Exiting Main Thread")
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
1.5.3 使用条件变量保持线程同步
Python的Condition对象提供了对复杂线程同步的支持,使用它可以在某些事件触发之后才处理数据,condition的方法有很多,看下面的例子:
用Condition来实现著名的生产者和消费者的关系:
#coding=utf-8
import threading
class Producer(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
global x
con.acquire()
if x==1000000:
con.wait()
pass
else:
for i in range(1000000):
x=x+1
con.notify()
print x
con.release() class Consumer(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
global x
con.acquire()
if x==0:
con.wait()
pass
else:
for i in range(1000000):
x=x-1
con.notify()
print x
con.release()
con=threading.Condition()
x=0
p=Producer('Producer')
c=Consumer('Consumer')
p.start()
c.start()
p.join()
c.join()
print x
结果如下:
>>>
1000000
0
0
1.5.4 使用队列保持线程同步
同样的例子,使用Queue可以实现多生产者和多消费者的先进先出的队列,每个生产者将数据依次存入队列,而每个消费者则依次从队列中取出数据,看下面的例子:
#coding=utf-8
import threading
import time
import Queue class Producer(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
global queue
queue.put(self.getName())
print self.getName(),'put',self.getName(),'to queue' class Consumer(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
global queue
print self.getName(),'get',self.getName(),'from queue' queue=Queue.Queue()
plist=[]
clist=[]
for i in range(10):
p=Producer('Producer'+str(i))
plist.append(p)
for i in range(10):
c=Consumer('Consumer'+str(i))
clist.append(c) for i in plist:
i.start()
i.join()
for i in clist:
i.start()
i.join()
看结果:
Producer0 put Producer0 to queue
Producer1 put Producer1 to queue
Producer2 put Producer2 to queue
Producer3 put Producer3 to queue
Producer4 put Producer4 to queue
Producer5 put Producer5 to queue
Producer6 put Producer6 to queue
Producer7 put Producer7 to queue
Producer8 put Producer8 to queue
Producer9 put Producer9 to queue
Consumer0 get Consumer0 from queue
Consumer1 get Consumer1 from queue
Consumer2 get Consumer2 from queue
Consumer3 get Consumer3 from queue
Consumer4 get Consumer4 from queue
Consumer5 get Consumer5 from queue
Consumer6 get Consumer6 from queue
Consumer7 get Consumer7 from queue
Consumer8 get Consumer8 from queue
Consumer9
Python_12-线程编程的更多相关文章
- Java面试题精选(二)线程编程、数据库理论和Jdbc部分
—— 线程编程.数据库理论和Jdbc部分内容 —— 数据库的开发应用想必是我们日常所碰到最多的知识点了,大致可分为:oracle.MySQL.SQL Server.Hadoop. NoSQL. ...
- Linux线程编程之信号处理
前言 Linux多线程环境中的信号处理不同于进程的信号处理.一方面线程间信号处理函数的共享性使得信号处理更为复杂,另一方面普通异步信号又可转换为同步方式来简化处理. 本文首先介绍信号处理在进程中和线程 ...
- java学习笔记15--多线程编程基础2
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...
- Java笔试题-线程编程方面
Ja 线程编程方面 60.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用? 答:有两种实现方法,分别是继承Thread类与实现R ...
- Posix线程编程指南
Posix线程编程指南 Posix线程编程指南... 1 一线程创建与取消... 2 线程创建... 2 1.线程与进程... 2 2. 创建线程... 2 3. 线程创建属性... 2 4. 创建的 ...
- Posix线程编程指南(2)
这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第2篇将向您讲述线程的创建与取消. 一.概念及作用在单线程程序中,我们经常要用到"全 ...
- Posix线程编程指南(3)
这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第三篇将向您讲述线程同步. 一.互斥锁尽管在Posix Thread中同样可以使用IPC的信号 ...
- Posix线程编程指南(1)
这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第一篇将向您讲述线程的创建与取消. 一.线程创建 1.1 线程与进程相对进程而言,线程是一 ...
- 在 POSIX 线程编程中避免内存泄漏
检测和避免 POSIX 线程内存泄漏的技巧 POSIX 线程(pthread)编程定义了一套标准的 C 编程语言类型.函数和常量 — 且 pthreads 提供了一种强大的线程管理工具.要充分使用 p ...
- POSIX 线程编程(一)简介
简介 在共享内存的多处理器结构中,可以用线程来实现并行.对于UNIX系统, IEEE POSIX 1003.1c标准规定了C语言线程编程接口的标准.这份标准的实现就是POSIX threads, 或者 ...
随机推荐
- 【转】理解Linux 配置文件
原文网址:http://www.mike.org.cn/articles/understanding-linux-configuration-files-linux/ 介绍每个 Linux 程序都是一 ...
- CF 1093E Intersection of Permutations——CDQ分治
题目:http://codeforces.com/contest/1093/problem/E 只能想到转化成查询一个区间里值在一个范围里的数的个数…… 没有想到这样适合用主席树套树状数组维护.不过据 ...
- 洛谷3354(IOI2005)河流——“承诺”
题目:https://www.luogu.org/problemnew/show/P3354 虽说是几个月前曾经讲过的题,但没有题解而自己(花了两个多小时)A了好高兴!!! 这是一个很好的套路:“承诺 ...
- Lombok 简单入门
原文地址:Lombok 简单入门 博客地址:http://www.extlight.com 一.前言 Lombok 是一个 Java 库,它作为插件安装至编辑器中,其作用是通过简单注解来精简代码,以此 ...
- vs2017 xamarin导入jar,SO文件的问题
最近要弄用vs弄个安卓的系统,因为要使用硬件,所以要引进jar,SO文件 导入jar文件很顺利,具体步骤我也是在网上找的这里给个链接 http://www.2cto.com/kf/201604/502 ...
- 轻量级封装DbUtils&Mybatis之四MyBatis主键
MyBatis主键 不支持对象列表存储时对自增id字段的赋值(至少包括3.2.6和3.3.0版本),如果id不是采用底层DB自增主键赋值,不必考虑此问题 温馨提示:分布式DB环境下,DB主键一般会采用 ...
- 一段四表联查外加字符拼接的sql,留存备查
select DISTINCT [P_ID],[P_CODE],[P_CODE_OLD],[P_NAME],[NATIVE_PLACE],[GENDER],[EDUCATION],[EMPLOY_DA ...
- C# 中的委托和事件(2)
委托.事件与Observer设计模式范例说明 上面的例子已不足以再进行下面的讲解了,我们来看一个新的范例,因为之前已经介绍了很多的内容,所以本节的进度会稍微快一些: 假设我们有个高档的热水器,我们给它 ...
- js 点击 隐藏弹出层
document.onmousedown = function(e){ var ev = document.all ? window.event : e; var _con = $("#ci ...
- mac下的安装神奇 brew --例子 安装 mysql
da打开终端 输入bre 输入 bre search mysql (查找mysql版本) 输入 bre install mysql@5.6(选择mysql版本安装)