线程,进程,协程面试知识点

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
后者内部是协程,遇到阻塞通过协程实现自动切换,从而达到单线程下实现并发的效果
'''
原文地址:https://www.cnblogs.com/changwenjun-666/p/11391503.html