python-并发线程的同步和互斥

一.什么是并发?

在操作系统中,指一个时间段内有几个程序都处于已启动到运行结束之间的状态,并且这几个程序都是在同一个处理机上运行的,但任一个时间点却只有一个程序在处理机上执行。

注意并发与并行并不是同一个概念。并发是指一个时间段内同时运行,表示的 是一个区间,而并行是指在同一个时间点上都在运行,是一个点,并且并发在同一时间点上只能有一个程序在运行。 在实际应用中,多个线程往往会共享一些数据(如:内存堆栈、串口、文件等 ),并且线程间的状态和行为都是互相影响的。

并发线程的两种关系:同步与互斥。

 

二.同步-互斥-死锁的概念

✓ 互斥:

线程之间通过对资源的竞争,所产生的相互制约的关系,就是互斥关系。 这类线程间主要的问题就是互斥和死锁的问题。

 

✓ 同步:

进程之间不是相互排斥的关系,而是相互依赖的关系。换句话说,就是多 进程共享同一临界资源时,前一个进程输出作为后一个进程的输入,当第一个进 程没有输出时,第二个进程必须等待。因为当多个线程共享数据时,可能会导致 数据处理出错,

因此线程同步主要的目的就是使并发执行的各线程之间能够有效 的共享资源和相互合作,从而使程序的执行具有可再现性。

 

总的来说,两者的区别就是:

互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。

同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。

 

✓死锁

如果程序中多个线程相互等待对方持有的锁,而在得到对方的锁之前都不释放自己的锁,由此导致了这些线程不能继续运行,这就是死锁。 死锁的表现是:程序死循环。

防止死锁一般的做法是:如果程序要访问多个共享数据,则首先要从全局考虑定义一个获得锁的顺序,并且在整个程序中都遵守这个顺序。释放锁时,按加锁的反序释放即可。

所以必须是有两个及其以上的的并发线程,才能出现死锁,如果是多于2 个线程之间出现死锁,那他们请求锁的关系一定是形成了一个环,比如A 等B的锁,B等C的锁,C等A的锁。

 

三.互斥的栗子

import threading
import time

data = 0
lock = threading.Lock()  # 创建一个锁对象


def func():
    global data
    print("%s acquire lock...\n" % threading.currentThread().getName())
    if lock.acquire():
        print("%s get lock...\n" % threading.currentThread().getName())
        data += 1  # must lock
        time.sleep(2)  # 其它操作
        print("%s release lock...\n" % threading.currentThread().getName())
        # 调用release()将释放锁
        lock.release()

startTime = time.time()
t1 = threading.Thread(target = func)
t2 = threading.Thread(target = func)
t3 = threading.Thread(target = func)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
endTime = time.time()
print("used time is", endTime - startTime)

四.同步的例子

from queue import Queue  # 队列类 i
import threading
import time
import random

# 生成者线程
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)  # 向队列中添加数据
            # 产生一个0-2之间的随机数进行睡眠
            time.sleep(random.randrange(10) / 5)
        print("%s: %s finished!" % (time.ctime(), self.getName()))


# 消费者线程
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()

该模型的好处:

1、实现了生产者与消费者解耦和

2、平衡了生产力与消费力,即生产者可以一直不停地生产,消费者可以不停地处理,因为二者不再直接沟通的,而是跟队列沟通,从而提高程序整体处理数据的速度

 

五.死锁的例子

import threading
import time
lock1 = threading.Lock()
lock2 = threading.Lock()
print(lock1, lock2)

class T1(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.t_name = name

    def run(self):
        lock1.acquire()
        time.sleep(1)  # 睡眠的目的是让线程2获得调度,得到第二把锁
        print('in thread T1',self.t_name)
        time.sleep(2)
        lock2.acquire()  # 线程1请求第二把锁
        print('in lock l2 of T1')
        lock2.release()
        lock1.release()

class T2(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self )
        self.t_name = name

    def run(self):
        lock2.acquire()
        time.sleep(2)#睡眠的目的是让线程1获得调度,得到第一把锁
        print('in thread T2',self.t_name)
        lock1.acquire() #线程2请求第一把锁
        print('in lock l1 of T2')
        lock1.release()
        lock2.release()

if __name__== '__main__':
    thread1 = T1('A')
    thread2 = T2('B')
    thread1.start()
    thread2.start()
'''
实例中,在两个线程thread1和 thread2分别得到一把锁后,
然后在 线程1中请求线程2得到的那把锁, 
线程2中请求在线程1中得到的那把 锁,
由于两个线程都在请求对方的 锁,但却没有一方释放它们的锁, 
所以就会出现死锁的情况,程序执 行后就会出现下面程序一直等待的 结果。
'''

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值