day 36

生产者消费者补充

def make_hot_dog(name,q):
    for i in range(5):
        time.sleep(random.randint(1,3))
        print("%s 生产了di%s热狗"%(name,i))
        q.put("%s的第%s个热狗"%(name,i))       #将生产好的数据放入共享对列中

def eat(name,q):
    while True:
        hot_dog = q.get()         #将数据从共享队列中取出操作
        time.sleep(random.randint(1,2))
        print("%s吃了%s"%(name,hot_dog))
        q.task_done()            # 因为实在JoinableQueue中所以每取出一次数据后就用使用一次task_done()
                                #要与下方.join对应起来

if __name__ == '__main__':
    q = JoinableQueue()        #在主进程中设置一个公共的共享队列
    p = Process(target=make_hot_dog,args=("藤原热狗店",q))
    p.start()

    e = Process(target=eat,args=("阿花",q))
    # e.daemon = True
    e.daemon = True       #此处使用daemon将e进程设置为守护进程,是因为,此处消费者进程没有结束条件,当生产者结束以后
                          #消费者把队列中的数据取完以后,没有数据可取以后,程序又不会结束,就会卡在那里
    e.start()
    # e.daemon = True
    p.join()          #此处使用join是为了让主进程等待子进程结束,如果不加的话,主进程
                      # 主进程结束守护进程也会结束掉
    print("热狗做好了")
    q.join()          # 这里使用.join的是为了回应给主程序,消费者将队列中的数据取完了,通知之进程向下执行
    print("阿花吃完了")

什么是线程

线程是操作系统最小的运算调度单位,是存在与进程中的,一个线程就是一个固定的执行流程。

线程的进程的关系

线程是存在与内存中的,并不能单独存在,进程是一个资源单位,它包含了程序运行所需要的所有资源。

真正运行程序代码的是线程,如果没有线程,进程中的资源无法被利用起来,进程中至少会有一个线程,创建进程时

产生的线程被称为主线程,当我们启动程序时操作系统就会给进程创建一个主线程,我们自己因任务需要创建的线程被称为
子线程。

为什么需要线程

使用线程的目的只要一个,就是为了提高效率。可以把工厂车间看作是一个进程,在没有接触过线程时我们为了提高效率

会再创建一个或多个进程,来提高效率,有了线程后就不会这么想了,直接在进程里创建新的线程即可,就好像,工厂里为了提高

生产效率,肯定是能在车间例加设流水线而不是再建一个车间出来。

如何使用

使用方式上和使用进程基本相同,和线程不同的是开启线程的代码可以放在任意位置,不必放在判断下面,因为线程是在进程内部

执行代码,并不是像开启进程一样去创建一个新的内存空间。

from multiprocessing import Process
from threading import Thread,current_thread,Lock

#
#
def task():
    print("1",current_thread())     #子线程
    print("111111111")



# if __name__ == "__main__":
#只是在同一个进程内执行了另外一行代码,不加判断也可以
t = Thread(target=task)
t.start()
print("2222",current_thread())    #主线程




方法2:创建新的thread类并重新覆盖run方法
class MyThread(Thread):
    def run(self):
        print("445464654")


m = MyThread()
m.start()

线程的特点

创建线程的开销小,多个进程之间不存在子符关系,所有线程的PID都是相同的。同一个进程中的多个线程的数据是共享的。

# 同一个进程中线程间资源共享
a = 20
def task():
    global a
    print("子线程run>>>>>")
    a = 100
t1 = Thread(target=task)
t1.start()
t1.join()      #等待子进程执行结束,确保数据已被修改
print(a)

线程 互斥锁

同一个进程内的线程资源共享带来的问题就是资源的抢夺,线程中也会存在安全的问题,多线程可以并发执行,一旦执行,访问同一个同一个进程中的资源就会出现数据错乱的问题。

解决数据错乱和安全的问题的方案还是互斥锁。

# 线程的安全问题
num = 10
l = Lock()
def task():
    global num
    l.acquire()
    a = num
    time.sleep(1)
    num = a-1
    l.release()

if __name__ == "__main__":
    for i in range(10):
        t = Thread(target=task)
        t.start()
    for t in enumerate()[1:]:    #用于查看线程总数我们需要的是子线程,这里将主线程给通过切片拿出去
        print(t)
        t.join()     #等待所有子线程结束
    print(num

死锁问题

死锁是指程序中出现了不止一把锁,分别被不同的线程持有,有一个资源如果想要使用必须两把锁同时持有,这样的话程序就会进入卡死状态,这就是死锁。

例如

要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子
如何避免死锁问题 :
锁不要有多个,一个足够
如果真的发生了死锁问题,必须迫使一方先交出锁

import time
# 盘子
lock1 = Lock()

# 筷子
lock2 = Lock()

def eat1():
    lock1.acquire()
    print("%s抢到了盘子" % current_thread().name)
    time.sleep(0.5)
    lock2.acquire()
    print("%s抢到了筷子" % current_thread().name)

    print("%s开吃了!" % current_thread().name)
    lock2.release()
    print("%s放下筷子" % current_thread().name)

    lock1.release()
    print("%s放下盘子" % current_thread().name)


def eat2():
    lock2.acquire()
    print("%s抢到了筷子" % current_thread().name)

    lock1.acquire()
    print("%s抢到了盘子" % current_thread().name)


    print("%s开吃了!" % current_thread().name)


    lock1.release()
    print("%s放下盘子" % current_thread().name)
    lock2.release()
    print("%s放下筷子" % current_thread().name)


t1 = Thread(target=eat1)


t2 = Thread(target=eat2)

t1.start()
t2.start()

可重入锁

Rlock 称之为递归锁或者可重入锁

Rlock不是用来解决死锁问题的

与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire

 

from threading import RLock, Lock, Thread

# l = Lock()
#
# l.acquire()
# print("1")
# l.acquire()
# print("2")


l = RLock()

# l.acquire()
# print("1")
# l.acquire()
# print("2")

def task():
    l.acquire()
    print("子run......")
    l.release()


# 主线程锁了一次
l.acquire()
l.acquire()

l.release()
l.release()
t1 = Thread(target=task)
t1.start()

信号量


可以现在被锁定的代码 同时可以被多少线程并发访问。
Lock 锁住一个马桶 同时只能有一个。
Semaphore 锁住一个公共厕所 同时可以来一堆人。

用途:仅用于控制并发访问 并不能防止并发修改造成的问题

from threading import Semaphore, Thread
import time

s = Semaphore(5)
def task():
    s.acquire()
    print("子run")
    time.sleep(3)
    print("子over")
    s.release()

for i in range(10):
    t = Thread(target=task)
    t.start
原文地址:https://www.cnblogs.com/1624413646hxy/p/10974853.html