并发编程十二问

一、简述计算机操做系统中的“中断”的做用?

计算机操做系统的中断的做用:cpu会切:io阻塞,程序运行时间过长

    中断:计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得
cpu暂时中断当前正在执行的程序而转去执行相应的事件处理程序。
    
    待处理完毕后又返回原来被中断处理急需执行或者调度新的进程执行的过程,它使计算
机能够更好更快的利用有限的系统资源解决系统响应速度和运行效率的一种控制技术:
    实时响应 + 系统调用
    
    中断装置是由一些特定的寄存器和控制线路组成,中央处理器和外围设备等识别到的
事件保存在特定的寄存器中。
    中央处理器每执行完一条指令,均有中断装置判别是否有事件发生。
    若无事件发生,CPU继续执行。
    如有事件发生,则中断装置中断原占有CPU的程序的执行,让操做系统的处理事件服
务程序占用CPU,对出现的事件进行处理,事件处理完后,再让原来的程序继续占用CPU执行复制代码

二、简述计算机内存中的“内核态”和“用户态”;

操做系统的核心是内核,独立于普通的应用程序,内核能够访问受保护的内存空间,
也能够访问底层硬件设备的全部权限,为了保证用户进程不能直接操做内核,保证内核
的安全,操做系统将虚拟空间划分为两部分,一部分是内核空间,一部分是用户空间。

    内核态:运行操做系统的程序,os的数据存放
    
    用户态:运行用户程序,用户进程的数据存放

    用户态的应用程序能够经过三种方式来访问内核态的资源:
        1)系统调用
        2)库函数
        3)Shell脚本
    用户态到内核态的切换:
        1.系统调用        用户程序主动发起的 软中断 os.fork() process
        2.异常            被动的   当CPU正在执行运行在用户态的程序时,忽然发生某些预
先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核态执行相关的
异常事件,典型的如缺页异常。

        3.外围设备的硬中断  被动的   外围设备完成用户的请求操做后,会像CPU发出中断信号,
此时,CPU就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,
若是先前执行的指令是在用户态下,则天然就发生从用户态到内核态的转换。

复制代码

三、进程间通讯方式有哪些?

进程间通讯(IPC)
    消息队列(    队列 = 管道 + 锁)
    管道(使用消息传递的)
    有名管道(FIFO)
    信号量
    共享内存
    套接字(socket)复制代码

四、简述你对管道、队列的理解;

管道一般指无名管道
一、它是半双工的(即数据只能在一个方向上流动),具备固定的读端和写端
二、它只能用于具备亲缘关系的进程中通讯(也就是父与子进程或者兄弟进程之间)
三、数据不可反复读取了,即读了以后欢喜红区中就没有了
消息队列
一、消息队列是面向记录的,其中的消息具备特定的格式以及特定的优先级
二、消息队列独立于发送与接收进程。进程终止时,消息队列及其内容不会被删除。
三、消息队列能够实现消息随机查询。

    队列 = 管道 + 锁复制代码

五、请列举你知道的进程间通讯方式;

队列,信号量,Event事件,定时器Timer,线程queue,进程池线程池,异步调用+回调机制复制代码

六、什么是同步I/O,什么是异步I/O?

同步IO指的是同步传输 ,当发送一个数据请求时,会一直等待,直到有返回结果为止

        异步IO指的是异步传输 ,当发送一个数据请求时,会当即去处理别的事情,当有数据
处理完毕后,会自动的返回结果
 
    通常同步传输能保证数据正确性 ,而异步能最大化性能。 
    如给u盘复制一个大的数据文件,你开了缓冲优化,是异步 工做, 复制的快了, 
    你要是刚复制完了直接拔 会丢数据, 
     你要是关了,复制的慢了,但你要是关了缓冲优化,复制完了直接拔 不会丢数据,

异步IO
    用户进程发起read操做以后,马上就能够开始去作其它的事。而另外一方
面,从kernel的角度,当它受到一个asynchronous read以后,首先它会
马上返回,因此不会对用户进程产生任何block。而后,kernel会等待数据
准备完成,而后将数据拷贝到用户内存,当这一切都完成以后,kernel会给
用户进程发送一个signal,告诉它read操做完成了。复制代码

七、请问multiprocessing模块中的Value、Array类的做用是什么?举例说明它们的使用场景

一般,进程之间彼此是彻底孤立的,惟一的通讯方式是队列或者管道,可是可使用两个对象来表示共享数据。其实这些对象使用了共享内存(经过mmap模块)使访问多个进程成为可能。python

Value( typecode, arg1, … argN, lock ) 

    在共享内容中常见ctypes对象。typecode要么是包含array模块使用的相同类型代码
(如’i’,’d’等)的字符串,要么是来自ctypes模块的类型对象(如ctypes.c_int、
ctypes.c_double等)。
    全部额外的位置参数arg1, arg2 ….. argN将传递给指定类型的构造函数。lock是只能
使用关键字调用的参数,若是把它置为True(默认值),将建立一个新的锁定来包含对值的访问。
    若是传入一个现有锁定,好比Lock或RLock实例,该锁定将用于进行同步。若是v是Value建立
的共享值的实例,即可使用v.value访问底层的值。例如,读取v.value将获取值,而赋值v.value
将修改值。

  RawValue( typecode, arg1, … ,argN) 
同Value对象,但不存在锁定。

  Array( typecode, initializer, lock ) 
    在共享内存中建立ctypes数组。typecode描述了数组的内容,意义与Value()函数中的相同。
initializer要么是设置数组初始大小的整数,要么是项目序列,其值和大小用于初始化数组。
lock是只能使用关键字调用的参数,意义与Value()函数中相同。
    若是a是Array建立的共享数组的实例,即可使用标准的python索引、切片和迭代操做访问它
的内容,其中每种操做均由锁定进行同步。对于字节字符串,a还具备a.value属性,能够吧整个
数组当作一个字符串进行访问。

  RawArray(typecode, initializer ) 
    同Array对象,但不存在锁定。当所编写的程序必须一次性操做大量的数组项时,若是同时
使用这种数据类型和用于同步的单独锁定(若是须要的话),性能将获得极大的提高。

    说到这里顺便给你们推荐一个Java架构方面的交流学习社群:650385180,里面不只能够交流讨论,
还有面试经验分享以及免费的资料下载,包括Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、
微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。相信对于已经工做和遇到技术瓶颈的码友,
在这个群里会有你须要的内容。复制代码

应该注意,使用多进程后,一般没必要再担忧与锁定、信号量或相似构造的底层同步,这一点与线程不相伯仲。在某种程度上,管道上的send()和receive()操做,以及队列上的put()和get()操做已经提供了同步功能。可是,在某写特定的设置下仍是须要用到共享值和锁定。下面这个例子说明了如何使用共享数组代替管道,将一个浮点数的python列表发送给另外一个进程:程序员

import multiprocessing
class FloatChannel(object):
    def __init__(self,maxsize):
        self.buffer=multiprocessing.RawArray('d',maxsize)
        self.buffer_len=multiprocessing.Value('i')
        self.empty=multiprocessing.Semaphore(1)
        self.full=multiprocessing.Semaphore(0)
    def send(self,values):
        self.empty.acquire()  #只在缓存为空时继续
        nitems=len(values)  
        self.buffer_len=nitems  #设置缓冲区大小
        self.buffer[:nitems]=values #将复制到缓冲区中
        self.full.release() #发信号通知缓冲区已满
    def recv(self):
        self.full.acquire()     #只在缓冲区已满时继续
        values=self.buffer[:self.buffer_len.value]  #复制值
        self.empty.release()        #发送信号通知缓冲区为空
        return values
    #性能测试 接收多条消息
def consume_test(count,ch):
    for i in xrange(count):
        values=ch.recv()

#性能测试 发送多条消息
def produce_test(count,values,ch):
    for i in xrange(count):
        ch.send(values)
if __name__=="__main__":
    ch=FloatChannel(100000)
    p=multiprocessing.Process(target=consume_test,args=(1000,ch))
    p.start()
    values=[float(x) for x in xrange(100000)]
    produce_test(1000,values,ch)
    print "Done"
    p.join()复制代码

八、请问multiprocessing模块中的Manager类的做用是什么?与Value和Array类相比,Manager的优缺点是什么?

能够经过使用Value或者Array把数据存储在一个共享的内存表中;Manager()返回一个manager类型,控制一个server process,能够容许其它进程经过代理复制一些python objects 支持list,dict,Namespace,Lock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value,Array ;面试

Manager类的做用共享资源,manger的的优势是能够在poor进程池中使用,缺点是windows下环境下性能比较差,由于windows平台须要把Manager.list放在if name =' main '下,而在实例化子进程时,必须把Manager对象传递给子进程,不然lists没法被共享,而这个过程会消耗巨大资源,所以性能不好。算法

multiprocessing 是一个使用方法相似threading模块的进程模块。容许程序员作并行开发。而且能够在UNIX和Windows下运行。windows

经过建立一个Process 类型而且经过调用call()方法spawn一个进程。数组

一个比较简单的例子:缓存

from multiprocessing import Process
import time
def f(name):
time.sleep(1)
print 'hello ',name
print os.getppid() #取得父进程ID
print os.getpid()  #取得进程ID
process_list = []
if __name__ == '__main__':
for i in range(10):
p = Process(target=f,args=(i,))
p.start()
process_list.append(p)
for j in process_list:
j.join()复制代码

进程间通讯:安全

有两种主要的方式:Queue、Pipe性能优化

1- Queue类几乎就是Queue.Queue的复制,示例:bash

from multiprocessing import Process,Queue
import time
def f(name):
time.sleep(1)
q.put(['hello'+str(name)])
process_list = []
q = Queue()
if __name__ == '__main__':
for i in range(10):
p = Process(target=f,args=(i,))
p.start()
process_list.append(p)
for j in process_list:
j.join()
for i in range(10):
print q.get()复制代码

2- Pipe 管道

from multiprocessing import Process,Pipe
import time
import os

def f(conn,name):
time.sleep(1)
conn.send(['hello'+str(name)])
print os.getppid(),'-----------',os.getpid()
process_list = []
parent_conn,child_conn = Pipe()
if __name__ == '__main__':
for i in range(10):
p = Process(target=f,args=(child_conn,i))
p.start()
process_list.append(p)
for j in process_list:
j.join()
for p in range(10):
print parent_conn.recv()复制代码

Pipe()返回两个链接类,表明两个方向。若是两个进程在管道的两边同时读或同时写,会有可能形成corruption.

进程间同步

multiprocessing contains equivalents of all the synchronization primitives from threading.

例如,能够加一个锁,以使某一时刻只有一个进程print

from multiprocessing import Process,Lock
import time
import os

def f(name):
lock.acquire()
time.sleep(1)
print 'hello--'+str(name)
print os.getppid(),'-----------',os.getpid()
lock.release()
process_list = []
lock = Lock()
if __name__ == '__main__':
for i in range(10):
p = Process(target=f,args=(i,))
p.start()
process_list.append(p)
for j in process_list:
j.join()复制代码

进程间共享状态 Sharing state between processes

固然尽最大可能防止使用共享状态,但最终有可能会使用到.

1-共享内存

能够经过使用Value或者Array把数据存储在一个共享的内存表中

from multiprocessing import Process,Value,Array
import time
import os

def f(n,a,name):
time.sleep(1)
n.value = name * name
for i in range(len(a)):
a[i] = -i
process_list = []
if __name__ == '__main__':
num = Value('d',0.0)
arr = Array('i',range(10))
for i in range(10):
p = Process(target=f,args=(num,arr,i))
p.start()
process_list.append(p)
for j in process_list:
j.join()
print num.value
print arr[:]
输出:
james@James:~/projects$ python pp.py 
81.0
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]复制代码

'd'和'i'参数是num和arr用来设置类型,d表示一个双精浮点类型,i表示一个带符号的整型。

更加灵活的共享内存可使用multiprocessing.sharectypes模块

Server process

Manager()返回一个manager类型,控制一个server process,能够容许其它进程经过代理复制一些python objects

支持list,dict,Namespace,Lock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value,Array

from multiprocessing import Process,Manager
import time
import os

def f(d,name):
time.sleep(1)
d[name] = name * name
print d
process_list = []
if __name__ == '__main__':
manager = Manager()
d = manager.dict()
for i in range(10):
p = Process(target=f,args=(d,i))
p.start()
process_list.append(p)
for j in process_list:
j.join()
print d
输出结果:
{2: 4}
{2: 4, 3: 9}
{2: 4, 3: 9, 4: 16}
{1: 1, 2: 4, 3: 9, 4: 16}
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 8: 64}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}复制代码

Server process managers比共享内存方法更加的灵活,一个单独的manager能够被同一网络的不一样计算机的多个进程共享。比共享内存更加的缓慢

使用工做池Using a pool of workers

Pool类表明 a pool of worker processes.

It has methods which allows tasks to be offloaded to the worker processes in a few different ways.

九、写一个程序,包含十个线程,子线程必须等待主线程sleep 10秒钟以后才执行,并打印当前时间;

# _*_ coding: utf-8 _*_ 
# 写一个程序,包含十个线程,子线程必须等待主线程sleep 10秒钟
# 以后才执行,并打印当前时间
from threading import Thread
import time

def task(name):
        print(name,time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))

if __name__ == '__main__':
    time.sleep(2)
    for i in range(10):
        t = Thread(target=task,args=('线程 %s'%i,))
        t.start()复制代码

十、写一个程序,包含十个线程,同时只能有五个子线程并行执行;

# _*_ coding: utf-8 _*_ 
# 十、写一个程序,包含十个线程,同时只能有五个子线程并行执行;
from threading import Thread
from threading import currentThread
from concurrent.futures import ThreadPoolExecutor
import time
import random

def task():
    print(currentThread().getName())
    time.sleep(random.randint(1,3))

if __name__ == '__main__':
    pool = ThreadPoolExecutor(5)
    for i in range(10):
        pool.submit(task)复制代码

十一、写一个程序,要求用户输入用户名和密码,要求密码长度很多于6个字符,且必须以字母开头,若是密码合法,则将该密码使用md5算法加密后的十六进制概要值存入名为password.txt的文件,超过三次不合法则退出程序;

# _*_ coding: utf-8 _*_ 
# 十一、写一个程序,要求用户输入用户名和密码,要求密码
# 长度很多于6个字符,且必须以字母开头,若是密码合法,
# 则将该密码使用md5算法加密后的十六进制概要值存入名
# 为password.txt的文件,超过三次不合法则退出程序;
import re
import hashlib
import pickle

def func():
    count = 0
    while count<3:
        username = input("username>>>").strip()
        password = input("password>>>").strip()
        if len(password) >=6 and re.search('\A([a-z)|[A-Z])',password):
            md5_password =  hashlib.md5(password.encode('utf-8')).hexdigest()
            file_obj = {'username':username,
                        'passworf':md5_password}
            f = open('password.txt','ab')
            pickle.dump(file_obj,f)
            break
        else:
            print("请输入合法的密码")
            count +=1
    else:
        print("您的机会已经用完")
if __name__ == '__main__':
    func()复制代码

十二、写一个程序,使用socketserver模块,实现一个支持同时处理多个客户端请求的服务器,要求每次启动一个新线程处理客户端请求;

服务端:

# _*_ coding: utf-8 _*_ 
# 十二、写一个程序,使用socketserver模块,
# 实现一个支持同时处理多个客户端请求的服务器,
# 要求每次启动一个新线程处理客户端请求;

import socketserver

class Handler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                data = self.request.recv(1024)
                if not data:
                    break
                print('client data',data.decode())
                self.request.send(data.upper())
            except Exception as e:
                print(e)
                break
if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(('127.0.0.1',8888),Handler)
    server.serve_forever()复制代码

客户端

# _*_ coding: utf-8 _*_ 
import socket
ip_port = ('127.0.0.1',8888)
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(ip_port)
while True:
    msg = input(">>>>").strip()
    if not msg:
        continue
    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print(data)复制代码
相关文章
相关标签/搜索