设为首页 收藏本站
查看: 943|回复: 0

[经验分享] 【转】Python多线程学习

[复制链接]

尚未签到

发表于 2017-4-28 09:12:51 | 显示全部楼层 |阅读模式
 一、Python
中的线程使用:

    
Python
中使用线程有两种方式:函数或者用类来包装线程对象。

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()
方法。

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
类的方法,线程的控制可以由自己定制。

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
模块本身也提供了很多方法和其他的类,可以帮助我们更好的使用和管理线程。可以参看
http://www.python.org/doc/2.5.2/lib/module-threading.html









假设两个线程对象
t1


t2

都要对
num=0

进行增
1

运算,
t1


t2

都各对
num

修改
10

次,
num

的最终的结果应该为
20

。但是由于是多线程访问,有可能出现下面情况:在
num=0

时,
t1

取得
num=0

。系统此时把
t1

调度为
”sleeping”

状态,把
t2

转换为
”running”

状态,
t2

页获得
num=0

。然后
t2

对得到的值进行加
1

并赋给
num

,使得
num=1

。然后系统又把
t2

调度为
”sleeping”

,把
t1

转为
”running”

。线程
t1

又把它之前得到的
0


1

后赋值给
num

。这样,明明
t1


t2

都完成了
1

次加
1

工作,但结果仍然是
num=1




    


上面的
case

描述了多线程情况下最常见的问题之一:数据共享。当多个线程都要去修改某一个共享数据的时候,我们需要对数据访问进行同步。


1、
  



简单的同步


最简单的同步机制就是“锁”。锁对象由
threading.RLock

类创建。线程可以使用锁的
acquire()

方法获得锁,这样锁就进入“
locked

”状态。每次只有一个线程可以获得锁。如果当另一个线程试图获得这个锁的时候,就会被系统变为“
blocked

”状态,直到那个拥有锁的线程调用锁的
release()

方法来释放锁,这样锁就会进入“
unlocked

”状态。“
blocked

”状态的线程就会收到一个通知,并有权利获得锁。如果多个线程处于“
blocked

”状态,所有线程都会先解除“
blocked

”状态,然后系统选择一个线程来获得锁,其他的线程继续沉默(“
blocked

”)。


Python

中的
thread

模块和
Lock

对象是
Python

提供的低级线程控制工具,使用起来非常简单。如下例所示:



import thread
import time
mylock = thread.allocate_lock()  #Allocate a lock
num=0  #Shared resource
def add_num(name):
global num
while True:
mylock.acquire() #Get the lock
# Do something to the shared resource
print 'Thread %s locked! num=%s'%(name,str(num))
if num >= 5:
print 'Thread %s released! num=%s'%(name,str(num))
mylock.release()
thread.exit_thread()
num+=1
print 'Thread %s released! num=%s'%(name,str(num))
mylock.release()  #Release the lock.
def test():
thread.start_new_thread(add_num, ('A',))
thread.start_new_thread(add_num, ('B',))
if __name__== '__main__':
test()

 
Python 


thread

的基础上还提供了一个高级的线程控制库,就是之前提到过的
threading


Python


threading module

是在建立在
thread module

基础之上的一个
module

,在
threading module

中,暴露了许多
thread module

中的属性。在
thread module

中,
python

提供了用户级的线程同步工具“
Lock

”对象。而在
threading module

中,
python

又提供了
Lock

对象的变种
: RLock

对象。
RLock

对象内部维护着一个
Lock

对象,它是一种可重入的对象。对于
Lock

对象而言,如果一个线程连续两次进行
acquire

操作,那么由于第一次
acquire

之后没有
release

,第二次
acquire

将挂起线程。这会导致
Lock

对象永远不会
release

,使得线程死锁。
RLock

对象允许一个线程多次对其进行
acquire

操作,因为在其内部通过一个
counter

变量维护着线程
acquire

的次数。而且每一次的
acquire

操作必须有一个
release

操作与之对应,在所有的
release

操作完成之后,别的线程才能申请该
RLock

对象。


下面来看看如何使用
threading


RLock

对象实现同步。



import threading
mylock = threading.RLock()
num=0
class myThread(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.t_name = name
def run(self):
global num
while True:
mylock.acquire()
print '/nThread(%s) locked, Number: %d'%(self.t_name, num)
if num>=4:
mylock.release()
print '/nThread(%s) released, Number: %d'%(self.t_name, num)
break
num+=1
print '/nThread(%s) released, Number: %d'%(self.t_name, num)
mylock.release()
def test():
thread1 = myThread('A')
thread2 = myThread('B')
thread1.start()
thread2.start()
if __name__== '__main__':
test()

 
我们把修改共享数据的代码成为“临界区”。必须将所有“临界区”都封闭在同一个锁对象的
acquire


release

之间。


2、
  



条件同步


锁只能提供最基本的同步。假如只在发生某些事件时才访问一个“临界区”,这时需要使用条件变量
Condition




Condition

对象是对
Lock

对象的包装,在创建
Condition

对象时,其构造函数需要一个
Lock

对象作为参数,如果没有这个
Lock

对象参数,
Condition

将在内部自行创建一个
Rlock

对象。在
Condition

对象上,当然也可以调用
acquire


release

操作,因为内部的
Lock

对象本身就支持这些操作。但是
Condition

的价值在于其提供的
wait


notify

的语义。


条件变量是如何工作的呢?首先一个线程成功获得一个条件变量后,调用此条件变量的
wait()

方法会导致这个线程释放这个锁,并进入“
blocked

”状态,直到另一个线程调用同一个条件变量的
notify()

方法来唤醒那个进入“
blocked

”状态的线程。如果调用这个条件变量的
notifyAll()

方法的话就会唤醒所有的在等待的线程。


如果程序或者线程永远处于“
blocked

”状态的话,就会发生死锁。所以如果使用了锁、条件变量等同步机制的话,一定要注意仔细检查,防止死锁情况的发生。对于可能产生异常的临界区要使用异常处理机制中的
finally

子句来保证释放锁。等待一个条件变量的线程必须用
notify()

方法显式的唤醒,否则就永远沉默。保证每一个
wait()

方法调用都有一个相对应的
notify()

调用,当然也可以调用
notifyAll()

方法以防万一。











生产者与消费者问题是典型的同步问题。这里简单介绍两种不同的实现方法。

1,  




条件变量


import threading
import time
class Producer(threading.Thread):
def __init__(self, t_name):
threading.Thread.__init__(self, name=t_name)

def run(self):
global x
con.acquire()
if x > 0:
con.wait()
else:
for i in range(5):
x=x+1
print "producing..." + str(x)
con.notify()
print x
con.release()

class Consumer(threading.Thread):
def __init__(self, t_name):
threading.Thread.__init__(self, name=t_name)
def run(self):
global x
con.acquire()
if x == 0:
print 'consumer wait1'
con.wait()
else:
for i in range(5):
x=x-1
print "consuming..." + str(x)
con.notify()
print x
con.release()

con = threading.Condition()
x=0
print 'start consumer'
c=Consumer('consumer')
print 'start producer'
p=Producer('producer')

p.start()
c.start()
p.join()
c.join()
print x

 
  


上面的例子中,在初始状态下,
Consumer

处于
wait

状态,
Producer

连续生产(对
x

执行增
1

操作)
5

次后,
notify

正在等待的
Consumer


Consumer

被唤醒开始消费(对
x

执行减
1

操作)



2,  




同步队列

Python

中的
Queue

对象也提供了对线程同步的支持。使用
Queue

对象可以实现多个生产者和多个消费者形成的
FIFO

的队列。

生产者将数据依次存入队列,消费者依次从队列中取出数据。


# producer_consumer_queue
from Queue import Queue
import random
import threading
import time

#Producer thread
class Producer(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread.__init__(self, name=t_name)
self.data=queue
def run(self):
for i in range(5):
print "%s: %s is producing %d to the queue!/n" %(time.ctime(), self.getName(), i)
self.data.put(i)
time.sleep(random.randrange(10)/5)
print "%s: %s finished!" %(time.ctime(), self.getName())

#Consumer thread
class Consumer(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread.__init__(self, name=t_name)
self.data=queue
def run(self):
for i in range(5):
val = self.data.get()
print "%s: %s is consuming. %d in the queue is consumed!/n" %(time.ctime(), self.getName(), val)
time.sleep(random.randrange(10))
print "%s: %s finished!" %(time.ctime(), self.getName())

#Main thread
def main():
queue = Queue()
producer = Producer('Pro.', queue)
consumer = Consumer('Con.', queue)
producer.start()
consumer.start()
producer.join()
consumer.join()
print 'All threads terminate!'

if __name__ == '__main__':
main()

 




在上面的例子中,
Producer

在随机的时间内生产一个“产品”,放入队列中。
Consumer

发现队列中有了“产品”,就去消费它。本例中,由于
Producer

生产的速度快于
Consumer

消费的速度,所以往往
Producer

生产好几个“产品”后,
Consumer

才消费一个产品。

Queue

模块实现了一个支持多
producer

和多
consumer


FIFO

队列。当共享信息需要安全的在多线程之间交换时,
Queue

非常有用。
Queue

的默认长度是无限的,但是可以设置其构造函数的
maxsize

参数来设定其长度。
Queue


put

方法在队尾插入,该方法的原型是:

put( 
item[, block[, timeout]])


如果可选参数
block


true

并且
timeout


None

(缺省值),线程被
block

,直到队列空出一个数据单元。如果
timeout

大于
0

,在
timeout

的时间内,仍然没有可用的数据单元,
Full exception

被抛出。反之,如果
block

参数为
false

(忽略
timeout

参数),
item

被立即加入到空闲数据单元中,如果没有空闲数据单元,
Full exception

被抛出。

Queue


get

方法是从队首取数据,其参数和
put

方法一样。如果
block

参数为
true


timeout


None

(缺省值),线程被
block

,直到队列中有数据。如果
timeout

大于
0

,在
timeout

时间内,仍然没有可取数据,
Empty exception

被抛出。反之,如果
block

参数为
false

(忽略
timeout

参数),队列中的数据被立即取出。如果此时没有可取数据,
Empty exception

也会被抛出。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-370195-1-1.html 上篇帖子: python学习小结1 下篇帖子: python yamal 配置格式
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表