Python线程并行编程

本文详细介绍了Python中的线程同步机制,包括Lock、RLock、Semaphore、Condition、Event、上下文管理器with以及Queue的使用方法,并通过实验展示了它们在多线程中的应用和效果,强调了使用队列作为线程安全的最佳实践。
摘要由CSDN通过智能技术生成

线程在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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值