day36

###################1.johnablequeue

from multiprocessing import JoinableQueue
# join是等待某个任务完成 able 可以怎么着 Queue 是队列

q = JoinableQueue()

q.put()放东西

q.get()取出东西

q.task_done()  #注意:该函数 不是表示任务全部处理完成  而是 取出来某个数据处理完成

q.join()   # 等待队列中的数据被处理完毕     join task_done的次数 == put的调用次数

"""

##生产者消费者模型JoinableQueue
要生成热狗
思聪负责吃热狗

"""
import random
import time
from multiprocessing import Process, Queue,JoinableQueue


def make_hotdog(name,q):
  for i in range(5):
  time.sleep(random.randint(1,3))
  print("%s生产了热狗%s" % (name,i))
  q.put("%s的%s号热狗" % (name,i))
# q.put(None)

def eat_hotdog(name,q):
  while True:
    hotdog = q.get()
    # if not hotdog:
    # break
    time.sleep(random.randint(1, 3))
    print("%s吃掉了%s" % (name,hotdog))
# 必须记录 该数据 处理完成了
    q.task_done()

if __name__ == '__main__':
  q = JoinableQueue()

  p = Process(target=make_hotdog,args=("owen的热狗店",q))
  p2 = Process(target=make_hotdog,args=("bgon的热狗店",q))
  p3 = Process(target=make_hotdog,args=("jerry的热狗店",q))

  c = Process(target=eat_hotdog,args=("思聪",q))
  c.daemon = True #####守护进程
  c2 = Process(target=eat_hotdog, args=("大葱", q))
  c2.daemon = True
  # c3 = Process(target=eat_hotdog, args=("二葱", q))

  p.start()
  p2.start()
  p3.start()

  c.start()
  c2.start()
  # c3.start()

# 目前的思路 是当商家做完以后 放一个None 作为结束标志 不够好 必须明确商家和消费者的个数


# 明确商家生成完毕 在明确消费者吃完了 就算结束
  p.join()
  print("第一家生成完毕")

  p2.join()
  print("第二家生成完毕")

  p3.join()
  print("第三家生成完毕")


  # 消费者吃完了
  q.join()
  print("消费吃完了!")
  print("美好的一天结束了!")

###################2.线程理论以及两种方式的应用

什么是线程

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

# 线程的进程的关系 重点

​ 线程不能单独存在 必须存在于进程中,

​ 进程是一个资源单位,其包含了运行程序所需的所有资源

​ 线程才是真正的执行单位

​ 没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程

​ 当我们启动一个程序时,操作系统就会自己为这个程序创建一个主线程

​ 线程可以由程序后期开启 ,自己开启线程称之为子线程

# 为什么需要线程 重点

​ 目的只有一个就是提高效率

​ 就像一个车间 如果产量更不上 就在造一条流水线

​ 当然可以再造一个新车间,那需要把原材料运过去 ,这个过程是非常耗时的

​ 所以通常情况是创建新的流水线 而不是车间 即 线程

# 如何使用 重点

​ 使用方法和多进程一模一样

​ 不过开启线程的代码可以放在任何位置 开启进程必须放在判断下面

from threading import Thread,current_thread
import time

def task():
  print("2",current_thread())
  print("子线程running")
  time.sleep(10)
  print("子线程over")

# 使用方法一 直接实例化Thread类
if __name__ == '__main__':
  t = Thread(target=task)
  t.start()

  # task()
  # 执行顺序不固定 如果开启线程速度足够快 可能子线程先执行
  print("主线程over")
  print("1",current_thread())

# 使用方法二 继承Thread 覆盖run方法
class MyThread(Thread):
  def run(self):
  print("子线程run!")
m = MyThread()
print("主线over")

###################3.线程特点

## 线程的特点 重点

1.创建开销小

2.同一个进程中的多个线程数据时共享的

3.多个线程之间,是平等的没有父子关系 所有线程的PID都是相同的

import os
from threading import Thread
from multiprocessing import Process

import time


def task():
  # print("hello")
  print(os.getpid())
  pass

if __name__ == '__main__':

  st_time = time.time()

  ts = []
  for i in range(100):
  t = Thread(target=task)
  # t = Process(target=task)
  t.start()
  ts.append(t)

for t in ts:
  t.join()

print(time.time()-st_time)
print("主over")
```

###同一线程中数据共享

from threading import Thread


a = 10

def task():
  global a
  print("子 running...")
  a = 20

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

t1.join() # 主线程等待子线执行完毕
print(a)

###################4.守护线程

 ## 守护线程 了解

​ 一个线程可以设置为另一个线程的守护线程

​ 特点: 被守护线程结束后守护线程也随之结束

```
# 守护线程会等到所有非守护线程结束后结束 ! 前提是除了主线程之外 还有后别的非守护
# 当然如果守护线程已经完成任务 立马就结束了

from threading import Thread
import time

def task():
  print("子1running......")
  time.sleep(100)
  print("子1over......")

def task2():
  print("子2running......")
  time.sleep(4)
  print("子2over......")

t = Thread(target=task)
t.daemon = True
t.start()

t2 =Thread(target=task2)
t2.start()

print("主over")

# 子 1 run 子2 run 主 子over 子2over 结束了


# 主线程代码执行 完毕后 不会立即结束 会等待其他子线程结束
# 主 会等待非守护线程 即t2
# 主线程会等待所有非守护线程结束后结束

 

# 守护线程会等到所有非守护线程结束后结束 ! 前提是除了主线程之外 还有后别的非守护
# 当然如果守护线程已经完成任务 立马就结束了

# 皇帝如果活着 守护者 妃子死了 皇帝正常运行 皇帝死了 无论守护者是否完成任务 都立即结束

####################5.线程互斥锁,死锁

# 线程 互斥锁

​ 共享意味着竞争

​ 线程中也存在安全问题,

​ 多线程可以并发执行,一旦并发了并且访问了同一个资源就会有问题

​ 解决方案:还是互斥锁

from threading import Thread,enumerate,Lock
import time

number = 10

lock = Lock()

def task():
  global number
  lock.acquire()
  a = number
  time.sleep(0.1)
  number = a - 1
  lock.release()

for i in range(10):
  t = Thread(target=task)
  t.start()

for t in enumerate()[1:]:          #### 用于访问当前正在运行的所有线程
  # print(t)
  t.join()

print(number)

#死锁问题

死锁问题
当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源 要想使用必须同时具备两把锁
这时候程序就会进程无限卡死状态 ,这就称之为死锁
例如:
要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子

如何避免死锁问题
锁不要有多个,一个足够
如果真的发生了死锁问题,必须迫使一方先交出锁

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

######################6.信号量

信号量 了解
Lock RLock

可以现在被锁定的代码 同时可以被多少线程并发访问
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()

posted @ 2019-06-04 16:56  轩辕12  阅读(96)  评论(0编辑  收藏  举报