zoukankan      html  css  js  c++  java
  • 线程

    1.queue队列

    2.进程间通讯IPC机制

    3.什么是线程(理论)

    4.开启线程的俩种方式

    5.生产者消费者模型

    6.线程的其他方法

    7.线程间是可以通讯的

    8.守护线程

    9.互斥锁

    一.queue队列

     1.队列:先进先出

     2.堆栈:先进后出

     1 # get和put最好是一一对应,而且不要超过你传入的最大值,不会回进入阻塞态
     2 from multiprocessing import Queue
     3 
     4 q = Queue(5)# 括号内可以放参数,表示的是这个队列最大存储量
     5 
     6 q.put('q')
     7 q.put('w')
     8 q.put('e')
     9 q.put('r')
    10 q.put('t')
    11 q.put('t')# 如果队列被放满了,还继续往队列里面添加值,程序不会报错,会原地等待,直到有人取走值
    12 
    13 
    14 print(q.get())
    15 print(q.get())
    16 print(q.get())
    17 print(q.get())
    18 print(q.get())# 如果没有值取空了,在取的话,程序会进入阻塞状态,知道有人继续往队列中添加值
    19 
    20 
    21 '''
    22 还有几种方法(但这几种方法都不适用多进程的情况)
    23 q.full()判断队列是否满了
    24 print(q.empty())判断队列中的数据数据是否取完了
    25 print(q.get_nowait())取值,没有值不等待直接报错
    26 '''
    View Code
     

    二.进程间通讯IPC机制

    进程间通讯
    ​通讯指的就是交换数据
    进程之间内存是相互隔离的,当一个进程想要把数据给另外一个进程,就需要考虑IPC
    用队列可以测验出进程间是可以通过一些方法进行通讯的
    from multiprocessing import Process,Queue
    
    def qwe(q):
        q.put('hello ')
    
    
    def zxc(q):
        print(q.get(q))
    
    if __name__ == '__main__':
        q = Queue()
        p = Process(target=qwe,args=(q,))
        p1 = Process(target=zxc,args=(q,))
        p.start()
        p1.start()


    三.什么是线程

    什么是线程:
        线程是操作系统最小的运算调度单位,被包含在进程中,一个线程就是一个固定的执行流程
    
    线程和进程的关系:
        线程不能单独存在,必须存在于进程中,
    进程是一个资源单位,其中包含了运行程序所需要的所有资源
    
        线程才是真正的执行单位,没有线程,进程中的资源无法被利用起来,
    所以一个进程至少包含一个线程,称之为主线程,当我们启动一个程序时,
    操作系统就会自己为这个程序创建一个主线程,线程也可以由程序后期开启,
    自己开启的线程称之为子线程
    
    
    那位什么需要线程:
        开进程:
            1 申请内存空间 ,耗资源
            2 拷贝代码 ,耗资源
    
        开线程:
            目的只有一个就是提高效率,将内存比如工厂,那么进程就相当于是工厂里面的车间,
            而你的线程就相当于是车间里面的流水线,要是生产量跟不上的话,你肯定是创建新的流水线,
            而不是创建一个新的工厂
    
        一个进程内可以起多个线程,并且线程与线程之间的数据是共享的
        ps:开启线程的开销要远远小于开启进程的开销
    
    
    

    四.开启线程的俩种方式

    创建线程的俩种方式
    跟创建进程的方式是一样的

    一》

    from threading import Thread
    import time
    
    def task(name):
        print('%s is running'%name)
        time.sleep(2)
        print('%s is over'%name)
    # 注意:开启线程的时候不需要在__main__内
    t = Thread(target=task,args=('zy',))
    t.start()# 告诉操作系统开一个线程,线程的开销远远小于进程
    # 小到代码一执行完,线程就开启了
    print('主')

    二》

    from threading import Thread
    import time
    
    class MyThread(Thread):
        def __init__(self,name):
            super().__init__()
            self.name = name
    
        def run(self):
            print('%s is running'%self.name)
            time.sleep(2)
            print('%s is over'%self.name)
    
    t = MyThread('zy')
    t.start()
    print('主')

    五.生产者消费者模型

    生产者消费者是什么:

    生产者消费者
    生产者消费者模型是什么:
    模型,就是解决摸个问题的套路,
    产生数据的一方称之为生产者,
    处理数据的一方称之为消费者,
    列如:爬虫,生活中到处都是这种模型,
    饭店 厨师即使生产者,你吃饭的人就是消费者
    
    ## 生产者和消费者出啥问题了?   解决什么问题
    ​    生产者和消费,处理速度不平衡,一方快一方慢,导致一方需要等待另一方
    ## 生产者消费者模型解决这个问题的思路:     怎么解决
    ​    原本,双方是耦合 在一起,消费必须等待生产者生成完毕在开始处理, 反过来
    ​    如果消费消费速度太慢,生产者必须等待其处理完毕才能开始生成下一个数据
    
    ### 解决的方案:
    ​    将双方分开来.一专门负责生成,一方专门负责处理
    ​    这样一来数据就不能直接交互了 双方需要一个共同的容器
    ​    生产者完成后放入容器,消费者从容器中取出数据
    ​    这样就解决了双发能力不平衡的问题,做的快的一方可以继续做,不需要等待另一方

    列子:

    生产者:生产/制造数据的
    消费者:消费/处理数据的
    例子:做包子的,买包子的
    1.做包子远比买包子的多
    2.做包子的远比买包子的少
    如何解决供需不平衡的问题

    第一种解决方法:
     1 第一个版本
     2 from multiprocessing import Process,Queue,JoinableQueue
     3 import random
     4 import time
     5 
     6 
     7 def zuo(name,food,q):
     8     for i in range(10):
     9         data = '%s 生产了%s%s'%(name,food,i)
    10         time.sleep(random.random())
    11         q.put(data)
    12         print(data)
    13 
    14 def chi(name,q):
    15     while True:
    16         data = q.get()
    17         if data == None:break# 判断取到的是否为空
    18         print('%s吃了%s'%(name,data))
    19         time.sleep(random.random())
    20 
    21 if __name__ == '__main__':
    22     q = Queue()
    23     z = Process(target=zuo,args=('厨师zy','牛排',q))
    24     z1 = Process(target=zuo,args=('厨师xj','鸡排',q))
    25     c = Process(target=chi,args=('',q))
    26     c1 = Process(target=chi,args=('动物',q))
    27     z.start()
    28     z1.start()
    29     c.start()
    30     c1.start()
    31     z.join()
    32     z1.join()# 没有这个的话,直接取空,消费者就结束了,确保生产者确确实实生产完了
    33     q.put(None) # 存进去一个None,只要消费者取值得时候取到空了,就带表生产的都被吃完了,然后结束程序
    34     q.put(None)
    35 
    36 第一种方法的缺陷:没多一个人我就要多写一个None,要是人有成千上万怎么办
    View Code

    第二种方法:用JoinableQueue方法解决

    JoinableQueue里面的方法:

    JoinableQueue

    可以被join的队列

    join是等待任务结束

    队列怎么叫结束?

    调用task_done一次则表示有一个数据被处理完成了 当task_done次数等于put的次数就意味着任务处理完成了

    这也是join的执行时机

    该队列可以明确告知数据的使用方,所有数据都已经处理完成

    在生产者消费者模型中,解决了消费者不知道何时算是任务结束的问题

    具体过程:主进程先等待所有的生产者进程生成完毕,再等队列中的数据被全部处理,这就意味着,任务全部结束

     1 from multiprocessing import Process,JoinableQueue
     2 import random
     3 import time
     4 
     5 
     6 def zuo(name,food,q):
     7     for i in range(10):
     8         data = '%s 生产了%s%s'%(name,food,i)
     9         time.sleep(random.random())
    10         q.put(data)
    11         print(data)
    12 
    13 def chi(name,q):
    14     while True:
    15         data = q.get()
    16         print('%s吃了%s'%(name,data))
    17         time.sleep(random.random())
    18         q.task_done()# 告诉队列你已经从队列中取出了一个数据,并且处理完毕
    19 
    20 if __name__ == '__main__':
    21     q = JoinableQueue()
    22     z = Process(target=zuo,args=('厨师zy','牛排',q))
    23     z1 = Process(target=zuo,args=('厨师xj','鸡排',q))
    24     c = Process(target=chi,args=('',q))
    25     c1 = Process(target=chi,args=('动物',q))
    26     z.start()
    27     z1.start()
    28     c.daemon = True# 将消费者设为守护进程,主线程结束,子线程也就结束了
    29     c1.daemon = True
    30     c.start()
    31     c1.start()
    32     z.join()# 确保生产者确确实实生产完了
    33     z1.join()
    34 
    35     q.join()# 等待队列中数据全部取出
    View Code

    六.线程的其他方法

    from threading import Thread,current_thread,active_count
    import time
    import os
    
    def task(name,i):
        print('%s is running'%name)
        # print('子current_thread:',current_thread().name)# 查看子线程
        # print('子',os.getpid())
        time.sleep(i)
    
        print('%s is over'%name)
    # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
    t = Thread(target=task,args=('egon',1))
    t1 = Thread(target=task,args=('jason',2))
    t.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
    t1.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
    t1.join()  # 主线程等待子线程运行完毕
    print('当前正在活跃的线程数',active_count())# 查看正在活跃的线程数
    # 小的代码执行完 线程就已经开启了
    print('')
    # print('主current_thread:',current_thread().name)
    # print('主',os.getpid())# 查看线程id

    七.线程间是可以通讯的

    from threading import Thread
    
    money = 666
    
    def task():
        global money
        money = 999
    
    t = Thread(target=task)
    t.start()
    t.join()
    print(money)
    
     线程之间是可以通信的,数据之间不是相互隔离的
    结果是999

    八.守护线程

    一个线程可以设置为另一个线程的守护进程
    特点:被守护线程结束后守护线程也随之结束
    
    守护线程会等到所有非守护线程结束后结束!前提是除了主线程之外,还有其他的非守护线程
    当然如果守护线程已经完成任务,就会立马结束
     1 from threading import Thread
     2 import time
     3 
     4 def task():
     5     print("子1running......")
     6     time.sleep(5)
     7     print("子1over......")
     8 
     9 def task2():
    10     print("子2running......")
    11     time.sleep(4)
    12     print("子2over......")
    13 
    14 t = Thread(target=task)
    15 t.daemon = True
    16 t.start()
    17 
    18 t2 = Thread(target=task2)
    19 t2.start()
    20 print("主over")
    View Code

    九.互斥锁

    线程互斥锁
    不管是进程还是线程,只要共享就意味着竞争,线程中也存在安全问题,
    多线程可以并发执行,一旦并发了并且访问了同一个资源就会出现问题,
    解决方案:还是要加互斥锁
     1 from threading import Thread,Lock
     2 import time
     3 
     4 n = 100
     5 
     6 def task(mutex):
     7     global  n
     8     mutex.acquire()
     9     tmp = n
    10     time.sleep(0.1)
    11     n = tmp - 1
    12     mutex.release()
    13 
    14 t_list = []
    15 mutex = Lock()
    16 for i in range(100):
    17     t = Thread(target=task,args=(mutex,))
    18     t.start()
    19     t_list.append(t)
    20 for t in t_list:
    21     t.join()
    22 print(n)
    23 
    24 开100个线程,每一次都去减一,最终打印结果为0,如果不加锁的话打印结果为99,因为每个线程拿到的都是100,100减1等于99
    View Code
  • 相关阅读:
    南京网络赛a(离线 树状数组)
    super_log(欧拉降幂)
    ac自动机模板 hdu2222
    Seek the Name, Seek the Fame POJ
    Power Strings(求循环次数最多的循环节 kmp)
    Period(循环节 nxt数组的应用)
    kmp 算法回顾(Number Sequence HDU
    最短路 2 (百度之星 初赛3 B)(floyd)
    path(优先队列,dijk拓展)
    (银联复赛)爱喝「肥宅快乐水」的班长
  • 原文地址:https://www.cnblogs.com/zahngyu/p/11342197.html
Copyright © 2011-2022 走看看