线程在python中来自模块 threading.Thread
在python中的Thread的实现很简单,只要定义个函数就可以实现一个线程。如果定义一个线程类的话,需要继承Thread,先实现个类试试代码如下:
from threading import Thread
from time import sleep
class Cookbook(Thread):#继承Thread
def __init__(self):
Thread.__init__(self)
self.msn="hello thread"
def print_msn(self):
print(self.msn)
def run(self):# 类start() 时会自动运行run()函数
print('thread start')
x=0
while x<3:
self.print_msn()
sleep(1)#延时
x+=1
print('end')
hello_python=Cookbook()
hello_python.start()
hello_python.join()
print("program terminated ")
上面介绍了如何定义一个线程类,其实线程的实现还可以更简单。定义一个函数,放到 t=threading.Thread(target=函数).接着就可以对线程进行操作了。代码如下定义了两个线程:
############################ 如何开辟线程
#def func()
#t=threading.Thread(target=func)
from time import sleep
import threading
def first_f():
print(threading.currentThread().getName()+str(" is starting\n"))
sleep(2)#延时2秒
print(threading.currentThread().getName()+str(" is ending\n"))
return
def second_f():
print(threading.currentThread().getName()+str(" is starting\n"))
sleep(2)
print(threading.currentThread().getName()+str(" is ending\n"))#name命名后可以用currentThread().getName()来获取
return
if __name__=="__main__":
t1=threading.Thread(name="frist_f",target=first_f) #name对线程命名
t2=threading.Thread(name="second_f",target=second_f)
t1.start()
t2.start()
t1.join()
t2.join()
好了知道如何开辟线程了,我们要处理一个进程下如何共享进程的共享资源,而相互之间不冲突,或者说如何保证大家在管控下都能访问(保证线程安全)。 python中提供的线程同步方式,特别多例如:锁:Lock,Rlock,信号量:Semaphore, 条件:Condition,事件:Event,上下文管理器with ,队列:Queue
其中有些是容易导致死锁的。最佳方式是使用队列。下面我们将一个一个来试一试;
1.Lock:
我们将建立一组对比实验,一组线程使用锁来保持同步,而一组不适用锁保持同步;代码如下:
import threading
share_resource_with_lock=0#有锁线程的共享资源
share_resource_no_lock=0#无锁线程的共享资源
cnt=1000000#cnt越大 结果差距会更加明显
share_resource_lock=threading.Lock()
#######锁管理
def increment_with_lock():
global share_resource_with_lock#锁管理的资源
for i in range(cnt):
share_resource_lock.acquire()#上锁 防止其他线程对其(对谁上锁:上锁之后操作的资源)进行操作
share_resource_with_lock+=1
share_resource_lock.release()#释放锁
def decrement_with_lock():
global share_resource_with_lock
for i in range(cnt):
share_resource_lock.acquire()#上锁 防止其他线程对其(对谁上锁:上锁之后操作的资源)进行操作
share_resource_with_lock-=1
share_resource_lock.release()#释放锁
######没有锁管理
def increment_lock():
global share_resource_no_lock # 锁管理的资源
for i in range(cnt):
share_resource_no_lock += 1
def decrement_lock():
global share_resource_no_lock
for i in range(cnt):
share_resource_no_lock -= 1
if __name__=='__main__':
t1=threading.Thread(target=increment_with_lock)
t2=threading.Thread(target=decrement_with_lock)
t3=threading.Thread(target=increment_lock)
t4=threading.Thread(target=decrement_lock)
t1.start()
t2.start()
t3.start()
t4.start()
t1.join()
t2.join()
t3.join()
t4.join()
#使用lock的线程是安全的 结果为0 没有使用lock的线程是不安全的 结果未知 lock容易出现死锁
print("the value of share_resource_whit lock %d"%share_resource_with_lock)
print("the value of share_resource_no_lock %d"%share_resource_no_lock)
实验结果如下:
the value of share_resource_whit lock 0 #带锁 线程安全
the value of share_resource_no_lock 80377 #无锁 线程不安全
Process finished with exit code 0
2.Rlock:
在threading模块中,定义两种类型的琐:threading.Lock和threading.RLock。它们之间有一点细微的区别,通过比较下面两段代码来说明:
import threading
lock = threading.Lock() #Lock对象
lock.acquire()
lock.acquire() #产生了死琐。
lock.release()
lock.release()
import threading
rLock = threading.RLock() #RLock对象 #获取锁的线程释放锁 用Rlock(), lock() 可以是不容的线程对锁进行操作
rLock.acquire()
rLock.acquire() #在同一线程内,程序不会堵塞。
rLock.release()
rLock.release()
import threading
from time import sleep
class Box:
rlock=threading.RLock()
def __init__(self):
self.total_items=0
def execute(self,n):
Box.rlock.acquire()
self.total_items+=n
Box.rlock.release()
def add(self):
Box.rlock.acquire()
self.execute(2)
Box.rlock.release()
def remove(self):
Box.rlock.acquire()
self.execute(-2)
Box.rlock.release()
def adder(box,items):
while items>0:
print("adding 2 items in the box\n")
box.add()
sleep(1)
items-=1
def remover(box,items):
while items>0:
print("removing 2 items in the box\n")
box.remove()
sleep(1)
items-=1
if __name__=="__main__":
items=3
print("putting %s items in the box "%items)
box=Box()
t1=threading.Thread(target=adder,args=(box,items))
t2=threading.Thread(target=remover,args=(box,items))
t1.start()
t2.start()
t1.join()
t2.join()
print("%s items still remain in the box"% box.total_items)
实验结果:
putting 3 items in the box
adding 2 items in the box
removing 2 items in the box
adding 2 items in the box
removing 2 items in the box
removing 2 items in the box
adding 2 items in the box
0 items still remain in the box
Process finished with exit code 0 #线程安全
3.Semaophore信号量实现同步,容易出现死锁:
代码如下:
import random
from time import sleep
import threading
sem=threading.Semaphore(0)#初试化为0
def consumer():
print("consumer is waitting")
#获取信号量
sem.acquire()
#访问共享资源
print("consumer notify :consumer item number %s "%item)
def producer():
global item
sleep(2)
item=random.randint(0,1000)
print("producer notify :producer item number %s"%item)
#释放信号量 将内部counter +1 当其值为0 时候 另一线程会等待期发生改变 其值大于0是会唤醒另一个线程
sem.release()
if __name__=="__main__":
for i in range(3):
t1=threading.Thread(target=producer)
t2=threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()
print("program terminated")
实验结果如下:
consumer is waitting
producer notify :producer item number 265
consumer notify :consumer item number 265
consumer is waitting
producer notify :producer item number 697
consumer notify :consumer item number 697
consumer is waitting
producer notify :producer item number 139
consumer notify :consumer item number 139
program terminated#线程安全
4.Condition 实现同步:
注意其操作 acquire()获取权限, wait()阻塞, notify()通知, release()释放,
from threading import Thread ,Condition
import time
item=[]#公共资源
con=Condition()
class consumer1(Thread):
def __init__(self):
Thread.__init__(self)
def consume(self):
global con
global item
con.acquire()
if len(item)==0:
con.wait()
print("consumer1 notify : no item to consume")
item.pop()
print("consumer1 notify: consumed 1 item ")
print("consumer1 notify: item to consume are "+str(len(item)))
con.notify()
con.release()
def run(self):
for i in range(10):
time.sleep(6)
self.consume()
class producer1(Thread):
def __init__(self):
Thread.__init__(self)
def produce(self):
global con
global item
con.acquire()
if len(item)==4:
con.wait() #阻塞 等待
print("producer1 notify: item produced are "+str(len(item)))
print("prodecer1 notify : stop the production")
item.append(1)
print("producer1 notify: item produced are " + str(len(item)))
con.notify() #通知
con.release() #释放
def run(self):
for i in range(10):
time.sleep(3)
self.produce()
if __name__=="__main__":
pro=producer1()
cons=consumer1()
pro.start()
cons.start()
pro.join()
cons.join()
print("program terminated")
实验结果如下:
producer1 notify: item produced are 1
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 0
producer1 notify: item produced are 1
producer1 notify: item produced are 2
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 1
producer1 notify: item produced are 2
producer1 notify: item produced are 3
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 2
producer1 notify: item produced are 3
producer1 notify: item produced are 4
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 3
producer1 notify: item produced are 4
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 3
producer1 notify: item produced are 3
prodecer1 notify : stop the production
producer1 notify: item produced are 4
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 3
producer1 notify: item produced are 3
prodecer1 notify : stop the production
producer1 notify: item produced are 4
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 3
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 2
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 1
consumer1 notify: consumed 1 item
consumer1 notify: item to consume are 0
program terminated
5.Event 实现同步:
代码流程图如下:
'''
Producer------> self.items.append(item) |---> self.event.wait()<--consumer
↑ | ↑ | ↑
| | | | |
| ↓ | ↓ |
| self.event.set()------| self.items.pop()-----|
| ↓
time.sleep() <------ self.event.clear()
'''
代码:
import time
from threading import Thread, Event
import random
items = []
event = Event()
class consumer(Thread):
def __init__(self, items, event):
Thread.__init__(self)
self.items = items
self.event = event
def run(self):
while True:
time.sleep(2)
self.event.wait()
item = self.items.pop()
print("consumer notify:%d poped from list by %s" % (item, self.name))
class producer(Thread):
def __init__(self, items, event):
Thread.__init__(self)
self.items = items
self.event = event
def run(self):
global item
for i in range(2):
time.sleep(5)
item = random.randint(0, 100)
self.items.append(item)
print("Producer notify: item N %d appended to list by %s" % (item, self.name))
self.event.set()
print("producer notify :envent set by %s" % self.name)
self.event.clear()
if __name__ == "__main__":
t1 = producer(items, event)
t2 = consumer(items, event)
t1.start()
t2.start()
t1.join()
t2.join()
print("program terminated") # self.event.wait() 一直在等待 所以不会打印这行
实验结果如下:
Producer notify: item N 98 appended to list by Thread-1
producer notify :envent set by Thread-1
consumer notify:98 poped from list by Thread-2
Producer notify: item N 20 appended to list by Thread-1
producer notify :envent set by Thread-1
consumer notify:20 poped from list by Thread-2
6.with 上下文管理器实现同步:
代码如下:
#上下文管理器 with 和线程有关系
# Lock Rlock Semaphore Condition
import threading
import logging
logging.basicConfig(level=logging.DEBUG,format="(%(threadName)-10s)%(message)s")
def threading_with(statement):
with statement:
logging.debug("%s acquired via with "%statement)
def threading_not_with(statement):
statement.acquire()
try:
logging.debug("%s acquired directly"%statement)
finally:
statement.release()
if __name__=="__main__":
lock=threading.Lock()
rlock=threading.RLock()
sem=threading.Semaphore(1)
con=threading.Condition()
syn_list=[lock,rlock,sem,con]
for statement in syn_list:
t1=threading.Thread(target=threading_with,args=(statement,))
t2=threading.Thread(target=threading_not_with,args=(statement,))
t1.start()
t2.start()
t1.join()
t2.join()
print("program terminated")
实验结果如下:
program terminated
(Thread-1 )<locked _thread.lock object at 0x000002882A1651E8> acquired via with
(Thread-2 )<locked _thread.lock object at 0x000002882A1651E8> acquired directly
(Thread-3 )<locked _thread.RLock object owner=8240 count=1 at 0x000002882A3B6E40> acquired via with
(Thread-4 )<locked _thread.RLock object owner=6664 count=1 at 0x000002882A3B6E40> acquired directly
(Thread-5 )<threading.Semaphore object at 0x000002882A400DA0> acquired via with
(Thread-6 )<threading.Semaphore object at 0x000002882A400DA0> acquired directly
(Thread-7 )<Condition(<locked _thread.RLock object owner=18964 count=1 at 0x000002882A3F91E8>, 0)> acquired via with
(Thread-8 )<Condition(<locked _thread.RLock object owner=6592 count=1 at 0x000002882A3F91E8>, 0)> acquired directly
Process finished with exit code 0
7.Queue 队列实现同步最佳方案:
队列使用起来容易,并且使得线程编程安全,因为他们会对单个线程对资源的多有访问进行过滤,并且支持更加整洁且可读性更棒的设计模式。put()添加元素到队尾,get()队首删除元素 task_done() 每次处理一个条目调用该方法,join()导致阻塞,直到所有条目处理完。
from queue import Queue
from threading import Thread
import time
import random
class producer(Thread):
def __init__(self,queue):
Thread.__init__(self)
self.queue=queue
def run(self):
for i in range(10):
item=random.randint(0,10)
self.queue.put(item)
print("producer notify :item %d appended to queue by %s"%(item,self.name))
time.sleep(1)
class consumer(Thread):
def __init__(self,queue):
Thread.__init__(self)
self.queue=queue
def run(self):
while True:
item=self.queue.get()
print("consumer notify: %d poped from queue by %s"%(item,self.name))
self.queue.task_done()
if __name__=="__main__":
queue=Queue()
t1=producer(queue)
t2=consumer(queue)
t3=consumer(queue)
t4=consumer(queue)
t1.start()
t2.start()
t3.start()
t4.start()
t1.join()
t2.join()
t3.join()
t4.join()
print("program terminated")
实验结果:
producer notify :item 6 appended to queue by Thread-1
consumer notify: 6 poped from queue by Thread-2
producer notify :item 5 appended to queue by Thread-1
consumer notify: 5 poped from queue by Thread-2
producer notify :item 9 appended to queue by Thread-1
consumer notify: 9 poped from queue by Thread-3
consumer notify: 6 poped from queue by Thread-4
producer notify :item 6 appended to queue by Thread-1
producer notify :item 10 appended to queue by Thread-1
consumer notify: 10 poped from queue by Thread-2
producer notify :item 1 appended to queue by Thread-1
consumer notify: 1 poped from queue by Thread-3
producer notify :item 2 appended to queue by Thread-1
consumer notify: 2 poped from queue by Thread-4
producer notify :item 0 appended to queue by Thread-1
consumer notify: 0 poped from queue by Thread-2
producer notify :item 10 appended to queue by Thread-1
consumer notify: 10 poped from queue by Thread-3
producer notify :item 8 appended to queue by Thread-1
consumer notify: 8 poped from queue by Thread-4