一.什么是并发?
在操作系统中,指一个时间段内有几个程序都处于已启动到运行结束之间的状态,并且这几个程序都是在同一个处理机上运行的,但任一个时间点却只有一个程序在处理机上执行。
注意并发与并行并不是同一个概念。并发是指一个时间段内同时运行,表示的 是一个区间,而并行是指在同一个时间点上都在运行,是一个点,并且并发在同一时间点上只能有一个程序在运行。 在实际应用中,多个线程往往会共享一些数据(如:内存堆栈、串口、文件等 ),并且线程间的状态和行为都是互相影响的。
并发线程的两种关系:同步与互斥。
二.同步-互斥-死锁的概念
✓ 互斥:
线程之间通过对资源的竞争,所产生的相互制约的关系,就是互斥关系。 这类线程间主要的问题就是互斥和死锁的问题。
✓ 同步:
进程之间不是相互排斥的关系,而是相互依赖的关系。换句话说,就是多 进程共享同一临界资源时,前一个进程输出作为后一个进程的输入,当第一个进 程没有输出时,第二个进程必须等待。因为当多个线程共享数据时,可能会导致 数据处理出错,
因此线程同步主要的目的就是使并发执行的各线程之间能够有效 的共享资源和相互合作,从而使程序的执行具有可再现性。
总的来说,两者的区别就是:
互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。
同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。
✓死锁
如果程序中多个线程相互等待对方持有的锁,而在得到对方的锁之前都不释放自己的锁,由此导致了这些线程不能继续运行,这就是死锁。 死锁的表现是:程序死循环。
防止死锁一般的做法是:如果程序要访问多个共享数据,则首先要从全局考虑定义一个获得锁的顺序,并且在整个程序中都遵守这个顺序。释放锁时,按加锁的反序释放即可。
所以必须是有两个及其以上的的并发线程,才能出现死锁,如果是多于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中得到的那把 锁,
由于两个线程都在请求对方的 锁,但却没有一方释放它们的锁,
所以就会出现死锁的情况,程序执 行后就会出现下面程序一直等待的 结果。
'''