zoukankan      html  css  js  c++  java
  • 线程,进程,协程面试知识点

    2. threading.local的作用?
    
    
    import threading
    # 创建全局ThreadLocal对象:
    localVal = threading.local()
    localVal.val = "Main-Thread"
    def process_student():
        print( '%s (in %s)' % (localVal.val, threading.current_thread().name))
    def process_thread(name):
        #赋值
        localVal.val = name
        process_student()
    t1 = threading.Thread(target= process_thread, args=('One',), name='Thread-A')
    t2 = threading.Thread(target= process_thread, args=('Two',), name='Thread-B')
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(localVal.val)
    
    #打印结果:
    '''
    One (in Thread-A)
    Two (in Thread-B)
    Main-Thread
    '''
    
    
    '''
    threading。local()这个方法:用来保存一个全局变量,但是这个变量只能被当前线程访问
    localVal.val = name这条语句可以储存一个变量到当前线程,如果在另外一个线程里面再次
    对localVal.val进行赋值,那么会在另外一个线程单独创建内存空间来存储,也就是说在不同
    的线程里面赋值 不会覆盖之前的值,因为每个线程里面都有一个单独的空间来保存这个数据,
    而且这个数据是隔离的,其他线程无法访问。本质上是在不同的线程在使用这个方法的时候为其
    创建了一个独立的内存空间!
    '''
    
    '''
    用处; 多线程下载,实现同时下载多个歌曲,用这个方法来保存每个下载线程的数据
    '''
    
    
    
    
    3. 进程之间如何进行通信?
    
    可以利用queue,joinablequeue,pipe来实现
    
    
    
    
    
    
    7、多道技术
    
    最初:操作系统将硬盘中的应用程序一个个读到内存,操作系统将内存中的应用程序交给cpu执行 ,遇到io就等待。
    多道技术:将硬盘上的多个数据全部读到内存中,操作系统为其划分不同的内存空间,此时不同内存空间内的应用程序,内存空间,操作系统对其的调度称为进程。
    
    空间复用:由于将不同的程序放到不同的内存空间,实现了空间复用
    
    时间复用:一个程序遇到io阻塞,操作系统会切换另外一个进程,交给cpu去执行 ,实现了时间复用
    
    8、分时系统
    
    即使当前在cpu里运行的程序没有遇到io,操作系统会在一定的时间后自动切换为另外一个进程 ,这样的切换其实没有提高cpu的效率,牺牲了一些效率,但是实现了多个程序共同执行的效果。
    
    9、进程的概念
    
    进程 是指应用程序,内存空间,操作系统的调度 称为一个进程。
    竞争计算机系统有限资源的基本单位,进行处理机调度的基本单位。
    
    
    10、并发与并行
    并发:单个cpu下+多道技术就可以实现并发,
    并行:同时运行,多核cpu才能实现并行
    
    
    11、同步  异步
    
    同步:提交任务后等待任务的返回结果,导致下一个任务只能在上一个任务结束后才能运行
    异步:,任务的提交互不影响
    
    12、阻塞 非阻塞
    
    阻塞:遇到io操作   等待是阻塞的副作用
    非阻塞:没有遇到io操作 
    
    
    13、同步阻塞 同步非阻塞 异步阻塞 异步非阻塞
    
    同步阻塞:相当于一个线程在等待
    同步非阻塞:相当于一个线程在正常运行
    异步阻塞:多个线程都在等待
    异步非阻塞:多个线程都在正常运行。
    
    
    
    
    14、io
    
    指的是读入和写出数据的过程,和等待读入/写出数据的过程,一旦拿到数据后就变成数据 操作了,就不是io了。即一个是等待数据的过程,一个是读写拷贝数据的过程。
    
    15、阻塞io  非阻塞io
    
    阻塞io:用户线程被阻塞在等待数据或拷贝数据上
    非阻塞io:用户线程没有因为io的事情出现阻塞,即数据已经拷贝好后,采取通知用户线程,一上来将就可以直接操作数据
        
    16、同步io   同步阻塞io
    
    同步io指的是发起io请求后,必须拿到io的数据才能继续执行。
    
    同步阻塞io:  在等待数据和拷贝数据过程中,线程都在阻塞,这就是同步阻塞io;  在等待数据的过程中,线程采用死循环式轮询,在拷贝数据的过程,线程在阻塞!
        
    在io上,同步和非阻塞是互斥的,所以不存在同步非阻塞io.
    
    所以,同步io一定是阻塞io,同步io也就是同步阻塞io.
        
    17、异步io  和 异步阻塞/非阻塞io
    
    异步io 发起io请求后,不用拿到io数据就可以继续执行。
    
    异步阻塞io:等待数据的过程,用户线程继续执行,拷贝数据的过程 ,线程在阻塞。
        
    异步非阻塞io: 等待和拷贝的过程中,用户线程都在继续执行。 本质上是因为用户线程既没有参与 等待,也没用参与拷贝的过程,所以是异步的。当其接到通知时,数据已经准备好了!没有因为io而阻塞,所以是非阻塞的。
    
    
    18、进程的创建:
    
    1)、系统初始化(前台进程负责与用户交互,后台 运行的进程与用户无关,运行在后台且需要时才被唤醒的进程,称为守护进程,如电子邮件,web页面)
    2)、一个进程在运行过程中开启了子进程,如nginx开启多进程
    3)、用户的交互式请求,如双击暴风影音
    4)、一个批处理作业的初始化
    
    19、进程的结束;
    
    正常退出
    出错退出
    严重错误   try...except捕获异常
    被其他进程杀死
    
    20、进程的同步部分
    
    进程之间数据不共享,但是共享同一套文件系统,同一个打印终端,共享带来的就是竞争,竞争带来的就是数据紊乱,可以加锁处理
    
    例如:开启多进程打印时,终端显示的数据有先有后,可以选择加锁让数据排列的整齐一些。
    
    
    21、进程的数据共享,进程间的数据通信
    
    1)、基于queue队列拉实现
    from multiprocessing import Queue
    q = Queue(3)
    
    q.put(3)
    q.put(3)
    q.put(3)
    #q.put(4)  再放一个,队列已满,程序会停在这,直到数据被取走
    try:
        q.put_nowait(3)  # 使用这种 格式,队列满了不会阻塞,但是会直接报错
    except:
        print('队列满了')
    
    # 因此可以在取数据之前查看队列的状态
    print(q.full())  # 满了就True,否则为False
    
    print(q.get())  
    print(q.get())
    print(q.get())
    # print(q.get())  # 和put类似,队列空了的话,再取会阻塞
    
    try:
        q.get_nowait(3) # 可以使用这种格式,队列空了不会阻塞,但是会直接报错
    except:
        print('队列已空')
        
    #关于q.empty()
    # 在空队列上放置q.empty时,可能会返回True,因为存在无限小的延迟。
    
    
    # 基于队列生产者消费者模型,在队列空了以后,程序会阻塞,进程不会结束。消费者在取空队列后,一直处于死循环中的等待数据被生产!
        
        
    2) JoinableQueue
    from multiprocessing import JoinableQueue, Process
    import random, time
    
    
    def producer(name, food, q):
        for i in range(5):
            data = '%s拉出了%s,%s' % (name, food, i)
            time.sleep(random.randint(1, 3))
            print(data)
            q.put(data)  # 将数据放入队列中
    
    
    def consumer(name, q):
        while True:
            data = q.get()
            time.sleep(random.randint(1, 3))
            print('%s消化了%s' % (name, data))
            q.task_done()  # 告诉队列,已经将数据取出并消化完毕
    
    
    if __name__ == '__main__':
        q = JoinableQueue()  # 生产一个队列对象
        a1 = Process(target=producer, args=('tank', '披萨', q))
        a2 = Process(target=producer, args=('egon', '面包', q))
        b1 = Process(target=consumer, args=('owen', q))
        b2 = Process(target=consumer, args=('fuck', q))
        a1.start()
        a2.start()
        b1.daemon = True
        b2.daemon = True
        b1.start()
        b2.start()
        # 等待生产者生产完所有的数据
        a1.join()
        a2.join()
        # 等待队列里的数据全部取出
        q.join()
        print('主进程')
    
    '''
    q.join():生产者调用该方法进行阻塞,直到队列里的所有数据全部被拿走,
    阻塞将持续到队列里每一个项目均调用q.task_done()方法为止,也就是队列里的所有数据都被get取走了
    '''
    
     
    3)、管道
    from multiprocessing import Process,Pipe
    
    
    def f(conn):
         conn.send('hello boy') # 子进程发送的消息,任何数据类型都可以
         conn.close()
    
    
    if __name__ == '__main__':
         parent_conn,child_conn = Pipe()   # 建立管道,拿到管道的两端,双方都可以收发数据
         p = Process(target=f,args=(child_conn,))
         p.start()
         print(parent_conn.recv()) # 主进程接收的消息
         p.join()
    
    '''
    管道通信不安全:
    返回的两个通信对象,每个对象都有recv和send方法。
    如果两个进程或线程在同一端读取或写入数据,那么管道中的数据可能会损坏!
    '''
    
    22、信号量 Semaphore  
    23、event事件
        
    
    24、全局解释器锁GIL
    
    这是 针对cpython而言的,为了保证在同一时间内只能存在一个线程去执行。我程序员控制的同步数据是针对于可见的变量,而GIL同步的是解释器后台爱的不可见变量,例如为了进行垃圾回而维护的引用计数,如果没有GIL,那么可能出现线程切换导致对同一个对象释放两次的情况!GIL是解释器相关的,而不是python这种语言的特性!
    
    
    存在的意义:
    相当于执行权限,保护着线程之间共享的数据;
    拿到gil后才能拿到互斥锁,其他线程也可以拿到gil,但是发现互斥锁没有被释放的话,gil权限要立刻交出来!
    
    
    25、互斥锁
    
    用来实现对共享资源的同步访问。
    import threading
    r = reading.Lock()
    r.acquire()
    '''
    对公共数据的操作部分
    '''
    r.release()
    
    
    26、死锁
    
    两个或者两个以上的进程或线程在执行过程中,抢夺资源而造成的一种互相等待的现象,若无外力干预下,将一直保持这种状态!
    解决;采用递归锁
    
    27、递归锁
    from threading import Rlock
    
    Rlock内部维持着一个Lock和counter变量,后者记录acquire的次数,从而可以使得资源被多次require,
    直到一个线程所有的acquire都被release,其他线程才能获得资源
    
    
    28、线程之间的通信   线程队列
    
    
    '''
    import queue  队列
    q =  queue.Queue(3)  # 里面的参数可以不传
    
    q.put(1)
    q.put(2)
    q.put(3)
    try:
        q.put_nowait(4)
    except:
        print('队列已满')
    
    print(q.get())
    print(q.get())
    print(q.get())
    try:
        print(q.get_nowait())
    except:
        print('队列已空')
    
    # 结果  为先进先出
    '''
    
    
    '''
    import queue
    q = queue.LifoQueue()  # 类似于栈
    
    q.put(1)
    q.put(2)
    q.put(3)
    #q.put_nowait()
    
    print(q.get())
    print(q.get())
    print(q.get())
    # print(q.get_nowait())
    
    #结果:先进后出
    '''
    
    
    '''
    import queue  
    
    q = queue.PriorityQueue()  # 排序
    
    q.put((10,'a'))
    q.put((11,'b'))
    q.put((12,'a'))
    
    print(q.get())
    print(q.get())
    print(q.get())
    
    #对于优先级,元组里的第一个元素通常是数字,也可以是非数字之间去比较大小
    #比较的结果中,该元素越小,优先级越高
    '''
    
    
    29、线程池 进程池
    
    concurrent.futures模块提供了高度封装的 异步调用接口
    
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    
    pool = ThreadPoolExecutor()
    
    pool.submit(fn,*args,**kwargs).add_done_callback(fn)
    
    
    小点:
    map (func,*iterables,timeout=None) 取代佛如循环submit的操作
    
    add_done_callback(fn):回调函数
    result;回调函数拿到的形参.result  可以拿到上一个函数的返回值。
    shutdown(wait=True)
    相当于进程池的pool.close()+pool.join()操作
    
    wait = True  等待池里所有的任务执行完毕回收完资源后才继续
    wait = False  立即返回,不等待池子里的所有线程结束。
    但是不管wait为何值,整个程序都会等所有任务执行完毕。
    submit和map需在sutdown之前。
    
    
    30、协程
    
    本质上是线程,之前线程任务的切换是由操作系统来完成的,遇到io就切换,现在我们自己可以利用协程实现
    较操作系统来切换更少的开销。主要是用在 开关线程,创建寄存器,堆栈等,自己用程序去控制任务的切换!
    
    协程是一个用户态的轻量级的线程,线程由用户程序区控制调度。
    
    1)、关于生成器的知识点瑕疵复习的时候再去研究!!
    
    
    2)、yield实现协程 - 任务的切换与保存
    
    import time
    
    
    def consumer():
        while True:
            x = yield
            time.sleep(1)
            print('接收了数据{}'.format(x))
    
    
    def producer():
        g = consumer()
        next(g)  # 需要先得到初始化一次的生成器
        for i in range(10000):
            g.send(i)
            print('发送了数据{}'.format(i))
    start = time.time()
    
    producer()
    end = time.time()
    print(end-start)
    
    '''
    实际上利用yield仅仅只是实现了切换与保存状态,即并发的效果
    并没有节省i/o时间,没有提高效率
    '''
    
    
    3)、Greenlet
     对于上者,可以利用该模块去实现,不过同样遇到io就原地阻塞。没有实现遇到io就自动切换的功能
        
    4)、协程所在的库     Gevent
    
    Gevent是一个第三方库,可以通过其实现并发同步和 异步编程。
    
    
    4.1)简单用法:
        
        import gevent
    
    def eat(name):
        print('%s eat 1'%name)
        gevent.sleep(2)
        print('%s eat 2'%name)
    
    
    def play(name):
        print('%s play 1'%name)
        gevent.sleep(1)
        print('%s play 2'%name)
    
    if __name__ == '__main__':
        g1 = gevent.spawn(eat,'michael')
        g2 = gevent.spawn(play,'tank')
        gevent.joinall([g1,g2])
        print('主线程')
    
    # 结果:
    '''
    michael eat 1
    tank play 1
    tank play 2
    michael eat 2
    主线程
    '''
    
    
    '''
    之所以用到 gevent.sleep()
    是因为gevent可以识别这种类型的阻塞
    而time.sleep()或者其他类型的 阻塞是不能直接识别的,必须加上补丁
    '''
    
    
    4.2)、gevent之同步异步:
    
    from gevent import spawn, joinall, monkey
    
    monkey.patch_all()
    
    import time
    
    
    def task(pid):
        time.sleep(1)
        print('task %s done' % pid)
    
    
    def synchronous():  # 同步
        for i in range(10):
            task(i)
    
    
    def asynchronous():  # 异步的
        g_l = [spawn(task, i) for i in range(10,20)]
        joinall(g_l)
    
    
    if __name__ == '__main__':
        print('synchronous:')
        synchronous()
        print('asynchronous:')
        asynchronous()
    
    '''
    上面程序先是执行synchronous,然后在执行asynchronous
    后者内部是协程,遇到阻塞通过协程实现自动切换,从而达到单线程下实现并发的效果
    '''
  • 相关阅读:
    HUST-1350 Trie
    hihocoder-第六十一周 Combination Lock
    hihocoder-1196 : 高斯消元·二
    hihocoder-1195 : 高斯消元·一
    SPOJ
    HDU-5074
    UVALive
    POJ-2195
    UVALive
    POJ-1556
  • 原文地址:https://www.cnblogs.com/changwenjun-666/p/11391503.html
Copyright © 2011-2022 走看看