浅析 java 编程语言中的五种编程模型

概念

IO 是主存和外部设备(硬盘、终端和网络等)拷贝数据的过程。IO是操作系统的底层功能实现,底层通过I/O指令进行完成。

以下是5种类Unix下可用的I/O模型

1. 阻塞式I/O:Blocking IO

2. 非阻塞式I/O:nonblocking IO

3. I/O 复用(Select,poll epoll):IO multiplexing

4. 信号驱动式I/O(SIGIO):signal driven IO

5. 异步 I/O(posix 的 aio 系列函数):asynchromous IO

Blocking IO

在 Linux 中,默认情况下所有的 socket 都是 Blocking,一个典型的读操作流程大概是这样:

1. 通常涉及等待数据从网络到达。当所有等待数据到达时,它被复制到内核中的某个缓冲区

2. 把数据从内核缓冲区复制到应用程序缓冲区

当用户进程调用了 recvfrom 这个系统调用, kernel 就开始了 IO 的第一个阶段:准备数据。对于 network IO 来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的 UDP 包)。这个时候 kernel 就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当 kernel 一直等到数据准备好了,它就会将数据从kernel 中拷贝到用户内存,然后kernel返回结果,用户进程才解除 block 的状态,重新运行起来。 所以,Blocking IO 的特点就是在IO执行的两个阶段都被 block了

非阻塞式I/O

Linux 下,可以用过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行操作时,流程如下: 从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。 从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次 发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。

所以,用户进程第一个阶段不是阻塞的,需要不断的主动询问kernel数据好了没有;第二个阶段依然总是阻塞的。

IO 多路复用(NIO)

select、epoll 的好处就在于单个 process 就可以同时处理多个网络链接的 IO。

IO 复用和同步阻塞本质一样,不过利用了新的 Select 系统调用,由内核来负责本来是请求进程该做的轮训操作,看似不非阻塞IO还多了哥系统调用开销,不过因为支持多路IO才算提高了效率 也就是一个可以监听多个。 它的基本原理就是 select、epoll 这个 function会不断的轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程。它的流程如下图:

当用户线程调用select,那么整个进程会被阻塞,而同时,kernel会监视所有select负责的 socket =,当任何一个 socket 中的数据准备好了,select 就会返回,这个时候用户进程会调用 read 操作,将数据 kernel 拷贝到用户进程。 首先开启套接字的信号驱动式IO功能,并且通过sigaction(信号处理程序) 系统调用安装一个信号处理函数 ,该函数调用将立即返回,当前进程没有被阻塞 ,继续工作;当数据报准备好的时候,内核为该进程产生SIGIO 的信号,随后既可以在信号处理函数中调用recvfrom 读取数据报,并且通知主循环数据已经准备好等待处理;也可以直接通知主循环让它读取数据报;(其实就是一个待读取的通知和待处理的通知),基本不会用到。

异步IO(AIO)

多线程和多进程的模型虽然解决了并发的问题,但是系统不能无限的增加线程,由于系统的切换线程的开销恒大,所以,一旦线程数量过多,CPU的时间就花在线程的切换上,正真运行代码的时间就会减少,结果导致性能严重下降

由于我们要解决的问题是CPU高速执行能力和IO设备的龟速严重不匹配,多线程和多进程只是解决这一个问题的一种方法。 另一种解决IO问题的方法是异步IO,当代码需要执行一个耗时的IO操作时,他只发出IO指令,并不等待IO结果然后就去执行其他代码,一段时间后,当IO返回结果是,在通知CPU进行处理我们调用aio_read函数,给内核传递描述符,缓冲区指针,缓冲区大小,和文件偏移量,并且告诉内核当整个操作完成时如何通知我们,该函数调用后,立即返回,不会被阻塞

另一方面:从kernel的角度,当他收到一个aio_read之后,首先它立即返回,所以不会对用户进程产生block,然后kernel会等待数据准备完成,然后将数据拷贝到用户内存(copy由内核完成),当着一切完成后,kernel会给用户进程发送一个singal或者执行下一个基于线程回调函数来完成此次IO处理过程,告诉他read操作完成

面试题

SpringMVC工作原理?

1. 客户端发送请求到前端控制器 DispatcherServlet
2. DispatcherServlet 收到请求后,调用 HandlerMapping 处理器映射器,请求获取 handler
3. 处理器映射器根据 url 找到具体的处理器,生成处理器对象以及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet
4. DispatcherServlet 调用 HandlerAdapter 处理器适配器
5. HandlerAdapter 经过适配器调用 具体处理器(Handler,也叫后端控制器)
6. Handler 执行完成返回 ModelAndView
7. HandlerAdaper 将 Handler 执行结果 ModelAndView 返回给 DispatcherServlet
8. DispatcherServlet 将 ModelAndView 传给 ViewResolver 视图解析器 进行解析
9. ViewResolver 解析后返回具体 View
10. DispatcherServlet 对 view 进行 渲染视图(即将模型数据填充至视图中)
11. DispatcherServlet 响应用户

ArrayList 、LinkedList和Vector的区别?


一、同步异步,阻塞非阻塞区别联系

实际上同步与异步是针对应用程序与内核的交互而言的。同步过程中进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成。异步过程中进程触发IO操作以后,直接返回,做自己的事情,IO交给内核来处理,完成后内核通知进程IO完成。同步和异步针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。同步有阻塞和非阻塞之分,异步没有,它一定是非阻塞的。阻塞、非阻塞、多路IO复用,都是同步IO,异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。真正的异步IO需要CPU的深度参与。换句话说,只有用户线程在操作IO的时候根本不去考虑IO的执行全部都交给CPU去完成,而自己只等待一个完成信号的时候,才是真正的异步IO。所以,拉一个子线程去轮询、去死循环,或者使用select、poll、epool,都不是异步。

同步:执行一个操作之后,进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成,等待结果,然后才继续执行后续的操作。

异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。

阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。

非阻塞:进程给CPU传达任我后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询

二、IO模型(五种编程模型)

这里统一使用Linux下的系统调用recv作为例子,它用于从套接字上接收一个消息,因为是一个系统调用,所以调用时会从用户进程空间切换到内核空间运行一段时间再切换回来。默认情况下recv会等到网络数据到达并且复制到用户进程空间或者发生错误时返回,而第4个参数flags可以让它马上返回。

1、阻塞IO模型

使用recv的默认参数一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。A同学用杯子装水,打开水龙头装满水然后离开。这一过程就可以看成是使用了阻塞IO模型,因为如果水龙头没有水,他也要等到有水并装满杯子才能离开去做别的事情。很显然,这种IO模型是同步的。

2、非阻塞IO模型

改变flags,让recv不管有没有获取到数据都返回,如果没有数据那么一段时间后再调用recv看看,如此循环。B同学也用杯子装水,打开水龙头后发现没有水,它离开了,过一会他又拿着杯子来看看……在中间离开的这些时间里,B同学离开了装水现场(回到用户进程空间),可以做他自己的事情。这就是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(等着水将水杯装满),因此它还是同步IO。

3、IO复用模型

这里在调用recv前先调用select或者poll,这2个系统调用都可以在内核准备好数据(网络数据到达内核)时告知用户进程,这个时候再调用recv一定是有数据的。因此这一过程中它是阻塞于select或poll,而没有阻塞于recv,有人将非阻塞IO定义成在读写操作时没有阻塞于系统调用的IO操作(不包括数据从内核复制到用户空间时的阻塞,因为这相对于网络IO来说确实很短暂),如果按这样理解,这种IO模型也能称之为非阻塞IO模型,但是按POSIX来看,它也是同步IO,那么也和楼上一样称之为同步非阻塞IO吧。

这种IO模型比较特别,分个段。因为它能同时监听多个文件描述符(fd)。这个时候C同学来装水,发现有一排水龙头,舍管阿姨告诉他这些水龙头都还没有水,等有水了告诉他。于是等啊等(select调用中),过了一会阿姨告诉他有水了,但不知道是哪个水龙头有水,自己看吧。于是C同学一个个打开,往杯子里装水(recv)。这里再顺便说说鼎鼎大名的epoll(高性能的代名词啊),epoll也属于IO复用模型,主要区别在于舍管阿姨会告诉C同学哪几个水龙头有水了,不需要一个个打开看(当然还有其它区别)。

4、信号驱动IO模型

通过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数(在这里面调用recv)。D同学让舍管阿姨等有水的时候通知他(注册信号函数),没多久D同学得知有水了,跑去装水。是不是很像异步IO?很遗憾,它还是同步IO(省不了装水的时间啊)。

5、异步IO模型

调用aio_read,让内核等数据准备好,并且复制到用户进程空间后执行事先指定好的函数。E同学让舍管阿姨将杯子装满水后通知他。整个过程E同学都可以做别的事情(没有recv),这才是真正的异步IO。

一般来讲:阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,因为阶段2是阻塞的(尽管时间很短)。只有异步IO模型是符合POSIX异步IO操作含义的,不管在阶段1还是阶段2都可以干别的事。

关于如何浅析编程语言中的五种编程模型就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

```python
class BertPooler(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.dense = nn.Linear(config.hidden_size, config.hidden_size)
        self.activation = nn.Tanh()

    def forward(self, hidden_states):
        # We "pool" the model by simply taking the hidden state corresponding
        # to the first token.
        first_token_tensor = hidden_states[:, 0]
        pooled_output = self.dense(first_token_tensor)
        pooled_output = self.activation(pooled_output)
        return pooled_output
from transformers.models.bert.configuration_bert import *
import torch
config = BertConfig.from_pretrained("bert-base-uncased")
bert_pooler = BertPooler(config=config)
print("input to bert pooler size: {}".format(config.hidden_size))
batch_size = 1
seq_len = 2
hidden_size = 768
x = torch.rand(batch_size, seq_len, hidden_size)
y = bert_pooler(x)
print(y.size())
```

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值