【网络进阶】服务器模型Reactor与Proactor
文章目录
- 1. Reactor模型
- 2. Proactor模型
- 3. 同步IO模拟Proactor模型
在高并发编程和网络连接的消息处理中,通常可分为两个阶段:等待消息就绪和消息处理。当使用默认的阻塞套接字时(例如每个线程专门处理一个连接),这两个阶段往往是合并的。因此,处理套接字的线程需要等待消息就绪,这在高并发场景下导致线程频繁地休眠和唤醒,从而影响 CPU 使用效率。
为提高高并发编程效率,可将等待消息就绪和消息处理两个阶段分开。换言之,等待消息就绪的代码段与处理消息的代码段应分离。这要求套接字必须为非阻塞,否则处理消息的代码段可能导致线程在条件不满足时进入等待状态。实现等待消息就绪阶段的方法是线程主动查询,或让单个线程等待所有连接。这就引入了 I/O 多路复用技术,它可以同时处理多个连接,尽管仍可能需要等待并导致线程休眠,但由于它可以监控所有连接,所以当线程被唤醒时,必定有一些连接已准备好进行处理。
高性能服务器程序通常需要处理三类事件:I/O 事件,定时事件以及信号。为实现高效的事件处理,有两种主要模型:Reactor 和 Proactor。这两种模型分别采用不同的策略来解决等待消息就绪和消息处理阶段的问题,以提高服务器在高并发环境下的性能表现。
1. Reactor模型
首先让我们回顾一下常规函数调用的机制:程序调用某个函数,函数执行,程序等待,然后函数将结果和控制权返回给程序,程序继续处理。与此不同,Reactor(反应堆)是一种事件驱动的机制。它颠覆了传统的事件处理流程:应用程序不再主动调用某个 API 完成处理,而是将处理逻辑逆置。在这种模式下,应用程序需要提供相应的接口并将其注册到 Reactor 上。当相应的事件发生时,Reactor 会主动调用应用程序注册的接口,这些接口通常被称为“回调函数”。通过这种方式,Reactor 实现了事件驱动编程,提高了程序的响应速度和效率。
Reactor 模式是处理并发 I/O 的一种常见模式,适用于同步 I/O。其核心思想是将所有待处理的 I/O 事件注册到一个中心 I/O 多路复用器上,同时让主线程/进程在多路复用器上阻塞。当有 I/O 事件到来或准备就绪(如文件描述符或 socket 可读、写)时,多路复用器返回并将事先注册的相应 I/O 事件分发到对应的处理器中。这样,Reactor 模式能够实现高效的并发 I/O 处理,提高程序在高并发环境下的性能表现。
Reactor模型有三个重要的组件:
- Reactor:Reactor 是核心组件,负责管理事件循环和I/O事件分发。它接收客户端连接,监听I/O事件,并将这些事件分发给相应的事件处理器。Reactor可以是单线程或多线程的,根据具体应用场景和性能要求进行选择。
- Handlers:事件处理器(Handlers)是用于处理特定I/O事件的对象。每个处理器通常对应一个客户端连接或一个资源(如文件、套接字等)。处理器负责处理与它们关联的事件,如读取数据、处理业务逻辑、发送响应等。处理器可以是同步或异步的,取决于具体的实现方式。
- Demultiplexer:事件分离器(Demultiplexer)负责从操作系统中获取I/O事件,并将这些事件传递给Reactor。常见的事件分离器实现方式有 select, poll, epoll, kqueue 等。事件分离器使Reactor能够同时处理多个I/O事件,从而实现高并发性能。
事件处理的具体流程:
- Reactor 接收客户端连接,将连接的相关I/O事件注册到事件分离器(Demultiplexer)上。同时,Reactor 将事件处理器(Handlers)与这些I/O事件关联起来。
- 事件分离器(Demultiplexer)负责检测并收集操作系统中发生的I/O事件。当有I/O事件发生时,事件分离器将这些事件通知Reactor。
- Reactor 将收到的I/O事件与之前注册的事件处理器(Handlers)进行匹配。找到匹配的处理器后,Reactor 将事件分发给相应的处理器。
- 事件处理器(Handlers)收到分发的I/O事件后,执行相应的操作,如读取数据、处理业务逻辑、发送响应等。
- 事件处理完成后,Reactor 可以根据需要更新处理器与I/O事件之间的关联,以便继续处理后续事件。
Reactor模型的优点:
- 高并发性能:Reactor模式通过事件驱动的方式实现了高并发性能。事件分离器可以同时检测多个I/O事件,Reactor可以根据事件分离器的通知并行处理多个I/O事件,从而提高服务器的并发处理能力。
- 良好的可扩展性:Reactor模式可以通过增加线程数量或使用多个Reactor实例来提高系统的可扩展性。在多核处理器环境下,可以利用多线程Reactor模式有效地分摊负载,进一步提高系统性能。
- 资源利用率高:Reactor模式中,事件处理器只在有I/O事件发生时才执行操作。这样可以避免无效的轮询和资源浪费,提高系统资源的利用率。
- 易于管理和维护:Reactor模式将事件处理器和I/O事件分离,使得事件处理逻辑更加清晰和易于管理。此外,Reactor模式还可以实现处理器的动态注册和注销,便于系统的维护和扩展。
- 可适应不同场景:Reactor模式可以根据不同的应用场景和性能要求进行定制。例如,可以选择单线程或多线程Reactor,同步或异步处理器等,以满足特定应用的需求。
Reactor 模型在开发效率方面相较于直接使用 IO 复用有所提高。该模型通常采用单线程设计,目标是让单线程充分利用一颗 CPU 的全部资源。同时,它还带有一个优点,即在处理事件时,很多情况下无需考虑共享资源的互斥访问问题。然而,这个模型也存在明显的缺点。随着硬件的发展,摩尔定律不再适用,CPU 频率由于材料限制无法继续大幅提升,因此提升性能主要依赖于增加核数。在需要利用多核资源的程序中,Reactor 模型的表现可能会受到影响。
对于业务较简单的程序,如仅需访问提供并发访问的服务,可以直接启用多个反应堆,每个反应堆对应一颗 CPU 核心。在这种情况下,运行在各个反应堆上的请求互不相关,从而充分利用多核资源。例如,Nginx 这类的 HTTP 静态服务器就采用了这种策略。
在C++里使用Reactor模型实现的TCP回显服务器:
需要系统支持epoll,并且编译器支持C++11
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
#include <unistd.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>#define MAX_EVENTS 10class Reactor {
public:Reactor() {_epoll_fd = epoll_create1(0);if (_epoll_fd == -1) {perror("epoll_create1");exit(EXIT_FAILURE);}}~Reactor() {close(_epoll_fd);}void add_fd(int fd, uint32_t events) {struct epoll_event event;event.data.fd = fd;event.events = events;if (epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) {perror("epoll_ctl");exit(EXIT_FAILURE);}}void del_fd(int fd) {if (epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr) == -1) {perror("epoll_ctl");exit(EXIT_FAILURE);}}void run() {std::vector<struct epoll_event> events(MAX_EVENTS);while (true) {int n = epoll_wait(_epoll_fd, events.data(), MAX_EVENTS, -1);if (n == -1) {perror("epoll_wait");exit(EXIT_FAILURE);}for (int i = 0; i < n; i++) {if (events[i].events & EPOLLIN) {handle_input(events[i].data.fd);} else if (events[i].events & EPOLLOUT) {handle_output(events[i].data.fd);}}}}virtual void handle_input(int fd) = 0;virtual void handle_output(int fd) = 0;private:int _epoll_fd;
};class EchoReactor : public Reactor {
public:EchoReactor(int listen_fd) : _listen_fd(listen_fd) {add_fd(_listen_fd, EPOLLIN);}void handle_input(int fd) override {if (fd == _listen_fd) {struct sockaddr_in addr;socklen_t addrlen = sizeof(addr);int conn_fd = accept(_listen_fd, (struct sockaddr *)&addr, &addrlen);if (conn_fd == -1) {perror("accept");exit(EXIT_FAILURE);}make_socket_non_blocking(conn_fd);add_fd(conn_fd, EPOLLIN);} else {char buf[1024];ssize_t n = read(fd, buf, sizeof(buf));if (n <= 0) {if (n < 0) perror("read");close(fd);del_fd(fd);} else {_out_buffers[fd] = std::string(buf, n);del_fd(fd);add_fd(fd, EPOLLOUT);}}
}void handle_output(int fd) override {auto it = _out_buffers.find(fd);if (it != _out_buffers.end()) {ssize_t n = write(fd, it->second.c_str(), it->second.size());if (n <= 0) {if (n < 0) perror("write");close(fd);del_fd(fd);} else {it->second.erase(0, n);if (it->second.empty()) {del_fd(fd);add_fd(fd, EPOLLIN);}}}
}private:int make_socket_non_blocking(int sfd) {int flags = fcntl(sfd, F_GETFL, 0);if (flags == -1) {perror("fcntl");return -1;}flags |= O_NONBLOCK;if (fcntl(sfd, F_SETFL, flags) == -1) {perror("fcntl");return -1;}return 0;}int _listen_fd;std::map<int, std::string> _out_buffers;
};int main(int argc, char *argv[]) {if (argc != 2) {std::cerr << "Usage: " << argv[0] << " <port>" << std::endl;exit(EXIT_FAILURE);}int port = std::stoi(argv[1]);int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) {perror("socket");exit(EXIT_FAILURE);}int optval = 1;if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {perror("setsockopt");exit(EXIT_FAILURE);}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {perror("bind");exit(EXIT_FAILURE);}if (listen(listen_fd, SOMAXCONN) == -1) {perror("listen");exit(EXIT_FAILURE);}EchoReactor echo_reactor(listen_fd);echo_reactor.run();return 0;
}
使用telnet测试:
2. Proactor模型
在众多网络编程模型中,Reactor模型因其高并发性能、资源利用率高等优点而受到广泛关注。Reactor模型基于事件驱动的原理,通过Reactor、事件处理器(Handlers)和事件分离器(Demultiplexer)三个核心组件的协同作用,有效地处理大量并发连接和I/O操作。然而,在某些高负载场景下,Reactor模型可能会遇到事件处理瓶颈的问题,这使得我们需要寻求其他解决方案来进一步提升系统性能。
Proactor模型正是在这种背景下应运而生的一种高性能网络编程模型。与Reactor模型类似,Proactor模型同样采用了事件驱动的方式,但在事件处理的实现上有所不同。Proactor模型将异步I/O操作与事件处理器相结合,使得I/O操作可以在后台执行,从而进一步减少阻塞和提高系统性能。在Proactor模型中,事件处理的主要责任由操作系统承担,而应用程序则专注于处理业务逻辑。这种分工使得Proactor模型能够更好地应对高负载场景,为构建高性能网络服务器提供了一种有力的解决方案。
Proactor模型有三个重要的组件:
- Proactor:Proactor是核心组件,负责管理事件循环和I/O事件分发。它接收客户端连接,监听I/O事件,并将这些事件分发给相应的事件处理器。与Reactor模型相比,Proactor将大部分I/O操作的处理交给操作系统,从而进一步减少阻塞和提高系统性能。
- Asynchronous Handlers:异步事件处理器(Asynchronous Handlers)是用于处理特定I/O事件的对象。每个处理器通常对应一个客户端连接或一个资源(如文件、套接字等)。与Reactor模型中的事件处理器不同,Proactor模型中的处理器通过异步I/O操作与事件处理相结合,使得I/O操作可以在后台执行,进一步提高系统性能。
- Asynchronous Operation Processor:异步操作处理器(Asynchronous Operation Processor)是负责执行实际的异步I/O操作的组件。它与事件处理器协同工作,将I/O操作的执行与事件处理解耦,进一步减少阻塞和提高系统性能。
- Completion Dispatcher:完成分发器(Completion Dispatcher)负责在异步I/O操作完成后通知Proactor。当操作系统完成一个异步I/O操作时,它会将完成通知发送给Completion Dispatcher,而后者则将通知传递给Proactor。这样,Proactor可以将事件分发给相应的事件处理器,处理业务逻辑。
事件处理的具体流程:
- Proactor 接收客户端连接,并将连接的相关I/O事件注册到异步事件处理器(Asynchronous Handlers)上。
- 异步事件处理器(Asynchronous Handlers)启动异步I/O操作。此时,I/O操作的执行由异步操作处理器(Asynchronous Operation Processor)负责,与事件处理器解耦,使得I/O操作可以在后台执行。
- 异步操作处理器(Asynchronous Operation Processor)与操作系统协同工作,执行实际的异步I/O操作。这样,事件处理器可以在等待I/O操作完成的过程中处理其他任务,进一步提高系统性能。
- 当操作系统完成一个异步I/O操作时,它会将完成通知发送给完成分发器(Completion Dispatcher)。
- 完成分发器(Completion Dispatcher)收到通知后,将通知传递给Proactor。此时,Proactor将事件分发给相应的异步事件处理器(Asynchronous Handlers)。
- 异步事件处理器(Asynchronous Handlers)收到分发的I/O事件后,执行相应的操作,如读取数据、处理业务逻辑、发送响应等。
从上述处理流程中,我们可以发现 Proactor 模型的最大特点是采用异步 I/O。所有的 I/O 操作都由系统提供的异步 I/O 接口执行,工作线程仅负责处理业务逻辑。在 Proactor 模型中,用户函数启动一个异步文件操作并将其注册到多路复用器上。多路复用器关注的是异步读操作是否完成,而不是文件是否可读或可写。异步操作由操作系统完成,用户程序无需关心。当操作系统完成读文件操作,即将数据复制到用户先前提供的缓冲区后,便通知多路复用器相关操作已完成。多路复用器随后调用相应的处理程序处理数据。
尽管 Proactor 增加了编程的复杂度,但它提高了工作线程的效率。Proactor 可在系统态优化读写操作,利用 I/O 并行能力,从而实现高性能单线程模型。在 Windows 上,由于缺乏类似 epoll 的机制,因此采用 IOCP 支持高并发。由于操作系统进行了良好的优化,Windows 更常使用基于完成端口的 Proactor 模型实现服务器。而在 Linux 上,虽然 2.6 内核引入了 aio 接口,但实际效果并不理想。aio 的出现主要是为了解决 poll 性能不佳的问题,但实际测试表明,epoll 的性能高于 poll+aio,且 aio 无法处理 accept。因此,Linux 主要还是以 Reactor 模型为主。
在不使用操作系统提供的异步 I/O 接口的情况下,确实可以通过 Reactor 模拟 Proactor。区别在于:利用异步接口可以使用系统提供的读写并行能力,而在模拟情况下,这需要在用户态实现。具体的做法包括以下步骤:
- 注册读事件(同时提供一段缓冲区)。
- 事件分离器等待可读事件。
- 事件到来时,激活分离器,分离器立即读取数据并写入缓冲区,然后调用事件处理器。
- 事件处理器处理数据,删除事件(需要再用异步接口注册)。
值得注意的是,Boost.asio 库采用的是 Proactor 模型。然而,在 Linux 平台上,Boost.asio 使用 epoll 实现的 Reactor 来模拟 Proactor,并额外开辟一个线程来完成读写调度。这种做法在一定程度上结合了两种模型的优势,实现了较高的性能和灵活性。
在C++里使用Proactor模型实现的TCP回显服务器:
这个示例基于Boost.Asio库,它是一个跨平台的C++库,用于编写基于异步I/O操作的网络和低级I/O程序,使用前请确保你已经正确安装,并配置好共享库文件。
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>using boost::asio::ip::tcp;class session : public std::enable_shared_from_this<session> {
public:session(tcp::socket socket) : socket_(std::move(socket)) {}void start() { read(); }private:void read() {auto self(shared_from_this());socket_.async_read_some(boost::asio::buffer(data_, max_length),[this, self](boost::system::error_code ec, std::size_t length) {if (!ec) {write(length);}});}void write(std::size_t length) {auto self(shared_from_this());boost::asio::async_write(socket_, boost::asio::buffer(data_, length),[this, self](boost::system::error_code ec, std::size_t /*length*/) {if (!ec) {read();}});}tcp::socket socket_;enum { max_length = 1024 };char data_[max_length];
};class server {
public:server(boost::asio::io_context& io_context, short port): acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) {accept();}private:void accept() {acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket socket) {if (!ec) {std::make_shared<session>(std::move(socket))->start();}accept();});}tcp::acceptor acceptor_;
};int main(int argc, char* argv[]) {try {if (argc != 2) {std::cerr << "Usage: echo_server <port>\n";return 1;}boost::asio::io_context io_context;server s(io_context, std::atoi(argv[1]));io_context.run();} catch (std::exception& e) {std::cerr << "Exception: " << e.what() << "\n";}return 0;
}
使用telnet测试:
3. 同步IO模拟Proactor模型
- 主线程将 socket 上的读就绪事件注册到 epoll 内核事件表中。
- 主线程调用 epoll_wait 等待 socket 上的数据可读事件。
- 当 socket 上有数据可读时,epoll_wait 通知主线程。主线程循环读取 socket 上的数据,直到无更多数据可读,然后将读取到的数据封装为请求对象并插入请求队列。
- 在请求队列上等待的某个工作线程被唤醒,获取请求对象并处理客户请求,然后在 epoll 内核事件表中注册 socket 上的写就绪事件。
- 主线程调用 epoll_wait 等待 socket 的可写事件。
- 当 socket 可写时,epoll_wait 通知主线程。主线程将服务器处理客户请求的结果写入 socket。
两种模式的相同之处在于它们都涉及对某个 I/O 事件的通知(即通知某个模块,这个 I/O 操作可以进行或已经完成)。在结构上,两者也有共同点:demultiplexor 负责提交 I/O 操作(异步)、查询设备是否可操作(同步),当条件满足时,回调注册的处理函数。
它们的不同之处在于:在 Proactor(异步)情况下,回调注册的处理函数时,表示 I/O 操作已经完成;而在 Reactor(同步)情况下,回调注册的处理函数时,表示 I/O 设备可以进行某个操作(可读或可写),此时注册的处理函数开始提交操作。
在C++中,我们可以使用Boost.Asio库来使用步同步I/O模拟Proactor模型。以下是使用Boost.Asio实现Proactor模型的步骤:
- 安装Boost库:首先,确保已经安装了Boost库,并将其包含在项目中。Boost.Asio是Boost库的一部分。
- 包含所需的头文件:
#include <boost/asio.hpp> #include <boost/bind.hpp> #include <iostream> #include <vector>
- 创建异步回调函数:为了模拟Proactor模型,我们需要创建异步回调函数,该函数在异步操作完成时被调用。
void handle_read(const boost::system::error_code& error, std::size_t bytes_transferred) {if (!error) {std::cout << "Read: " << bytes_transferred << " bytes" << std::endl;} else {std::cerr << "Error: " << error.message() << std::endl;} }
- 使用Boost.Asio创建异步操作:使用Boost.Asio创建一个异步读取操作,并将其与异步回调函数关联。
boost::asio::io_context io_context; boost::asio::ip::tcp::socket socket(io_context); boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8080);socket.async_connect(endpoint, boost::bind(&handle_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
- 运行I/O上下文:运行I/O上下文以处理异步操作。
io_context.run();
完整的代码如下:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>void handle_read(const boost::system::error_code& error, std::size_t bytes_transferred) {if (!error) {std::cout << "Read: " << bytes_transferred << " bytes" << std::endl;} else {std::cerr << "Error: " << error.message() << std::endl;}
}int main() {try {boost::asio::io_context io_context;boost::asio::ip::tcp::socket socket(io_context);boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8080);socket.async_connect(endpoint, boost::bind(&handle_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));io_context.run();} catch (std::exception& e) {std::cerr << "Exception: " << e.what() << std::endl;}return 0;
}
测试使用的TCP服务器来接受上面客户端的连接:
#include <boost/asio.hpp>
#include <iostream>
#include <array>
#include <memory>class session : public std::enable_shared_from_this<session> {
public:session(boost::asio::ip::tcp::socket socket) : socket_(std::move(socket)) {}void start() { read(); }private:void read() {auto self(shared_from_this());socket_.async_read_some(boost::asio::buffer(data_),[this, self](boost::system::error_code ec, std::size_t length) {if (!ec) {read();}});}boost::asio::ip::tcp::socket socket_;std::array<char, 1024> data_;
};class server {
public:server(boost::asio::io_context& io_context, short port): acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) {accept();}private:void accept() {acceptor_.async_accept([this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) {if (!ec) {std::make_shared<session>(std::move(socket))->start();}accept();});}boost::asio::ip::tcp::acceptor acceptor_;
};int main() {try {boost::asio::io_context io_context;server srv(io_context, 8080);io_context.run();} catch (std::exception& e) {std::cerr << "Exception: " << e.what() << std::endl;}return 0;
}
makefile:
.PHONY:clean
all: client.cc server.ccg++ client.cc -o client -I/usr/local/include -L/usr/local/lib -lpthread -std=c++11g++ server.cc -o server -I/usr/local/include -L/usr/local/lib -lpthread -std=c++11
clean:rm -f client server
相关文章:

【网络进阶】服务器模型Reactor与Proactor
文章目录 1. Reactor模型2. Proactor模型3. 同步IO模拟Proactor模型 在高并发编程和网络连接的消息处理中,通常可分为两个阶段:等待消息就绪和消息处理。当使用默认的阻塞套接字时(例如每个线程专门处理一个连接),这两…...

使用div替代<frameset><frame>的问题以及解决办法
首先是原版三层框架的html: <html> <head> <title>THPWP</title> </head> <!-- 切记frameset不能写在body里面,以下代表首页由三层模块组成,其中第一层我是用来放菜单高度占比14%,中间的用作主…...

Verilog中的`define与`if的使用
一部分代码可能有时候用,有时候不用,为了避免全部编译占用资源,可以使用条件编译语句。 语法 // Style #1: Only single ifdef ifdef <FLAG>// Statements endif// Style #2: ifdef with else part ifdef <FLAG>// Statements …...

沃尔玛、亚马逊影响listing的转化率4大因素,测评补单自养号解析
1、listing的相关性:前期我们在找词,收集词的时候,我们通过插件来协助我们去筛选词。我们把流量高,中,低的关键词都一一收集,然后我们再进行对收集得来的关键词进行分析,再进行挑词,…...

静态分析和动态分析
在开发早期,发现并修复bug在许多方面都有好处。它可以减少开发时间,降低成本,并且防止数据泄露或其他安全漏洞。特别是对于DevOps,尽早持续地将测试纳入SDLC软件开发生命周期是非常有帮助的。 这就是动态和静态分析测试的用武之地…...

代码随想录_贪心_leetcode 1005 134
leetcode 1005. K 次取反后最大化的数组和 1005. K 次取反后最大化的数组和 给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组: 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以…...

笔记:对多维torch进行任意维度的多“行”操作
如何取出多维torch指定维度的指定“行” 从二维torch开始新建torch取出某一行取出某一列一次性取出多行取出连续的多行取出不连续的多行 一次取出多列取出连续的多列取出不连续的多列 考虑三维torch取出三维torch的任意两行(means 在dim0上操作)取出连续…...

【VSCode】1、VSCode 如何连接服务器
文章目录 一、安装 remote-ssh 插件二、直接连接三、配置 SSH 公匙,免密登录 一、安装 remote-ssh 插件 点击插件搜索框,搜 remote-ssh,点击安装 安装完成后就会出现下面的图标: 二、直接连接 点击加号,输入 ssh 连接…...

AI工具:通过智能实现工作和学习效率的革命化
AI工具是指一系列人工智能技术和工具,包括机器学习、深度学习、自然语言处理、计算机视觉等。这些工具可以帮助开发人员和数据科学家通过处理和分析海量数据来自动识别和解决问题,提供智能的决策和预测模型。常见的AI工具包括TensorFlow、PyTorch、Keras…...

static 和构造方法
文章目录 static构造方法内存中数据的存储方式示例 static 具体对象的属性,称之为对象属性,成员属性,实例属性。 具体对象的方法,称之为对象方法,成员方法,实例方法。 静态:static 和具体对…...

【Linux 裸机篇(八)】I.MX6U EPIT 定时器中断、定时器按键消抖
目录 一、EPIT 定时器简介二、定时器按键消抖 一、EPIT 定时器简介 EPIT 的全称是: Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器,它主要是完成周期性中断定时的。学过 STM32 的话应该知道, STM32 里面的…...

Web安全 XSS靶场搭建(玩转整个XSS环境.)
Web安全 XSS靶场搭建 XSS又叫CSS(Cross Site Script)跨站脚本攻击,指的是攻击者在Web页面,插入恶意JS代码,当用户浏览该页之时,嵌入其中JS代码就会被执行,从而达到攻击的目的.(包含…...

前端开发技术——DOM(上)
一.单选题(共4题,44.4分) 1 下列选项中,关于事件的描述错误的是() A、 事件指的是可以被JavaScript侦测到的行为 B、 事件驱动程序指的是事件触发后要执行的代码 C、 事件源是指触发的事件 D、 事件的种类称为事件…...

银河麒麟v10服务器版安装OpenDDS
1. OpenDDS简介 OpenDDS是OMG数据分发服务(DDS)的一种开源实现,它遵循实时系统v1.2的DDS规范(OMG Document formal/07-01-01)和实时公布/订阅互操作性通信协议v2.1的DDS-RTPS规范(OMG Document formal/2010-11-01)。OpenDDS由OCI公司设计和维护,可从http…...

curl方式调用电商API接口示例 详细介绍
cURL是一个利用URL语法在命令行下工作的文件传输工具,1997年首次发行。它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下载工具。cURL还包含了用于程序开发的libcurl。 cURL支持的通信协议有FTP、FTPS、HTTP、H…...

Duboo介绍与入门
文章目录 1、Dubbo的前世今生2、Dubbo的快速入门2.1、Dubbo的基本架构2.2、Nacos2.3、管理后台2.4、入门案例2.4.1、服务提供者搭建环境代码实现配置文件 2.4.2、服务消费者搭建环境代码实现配置文件 最后说一句 1、Dubbo的前世今生 2011年10月27日,阿里巴巴开源了…...

人工智能中(Pytorch)框架下模型训练效果的提升方法
大家好,我是微学AI,今天给大家介绍一下人工智能中(Pytorch)框架下模型训练效果的提升方法。随着深度学习技术的快速发展,越来越多的应用场景需要建立复杂的、高精度的深度学习模型。为了实现这些目标,必须采用一系列复杂的技术来提…...

树莓派CSI摄像头使用python调用opencv库函数进行运动检测识别
目录 一、完成摄像头的调用 二、利用python调用opencv库函数对图像进行处理 2.1 图像处理大体流程 2.2 opencv调用函数的参数以及含义 2.2.1 ret, img cap.read() 读取帧图像 2.2.2 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 灰度图像 2.2.3 gray_diff_img cv2.absdiff(g…...

Parameters(in)、Parameters(out) and Parameters(inout)
0前言 参数类型(Parameters)指的是函数参数在调用时所具有的性质,从而对函数的调用方式产生影响。在 C 语言中,存在三种不同类型的函数参数:Parameters(in)、Parameters(out) 和 Parameters(inout) 1定义 Parameter…...

jstat命令查看jvm内存情况及GC内存变化
命令格式 jstat [Options] pid [interval] [count] 参数说明: Options,选项,一般使用 -gc、-gccapacity查看gc情况 pid,VM的进程号,即当前运行的java进程号 interval,间隔时间(按该时间频率自动刷新当前内存…...

java 图形化小工具Abstract Window Toolit :画笔Graphics,画布Canvas(),弹球小游戏
画笔Graphics Java中提供了Graphics类,他是一个抽象的画笔,可以在Canvas组件(画布)上绘制丰富多彩的几何图和位图。 Graphics常用的画图方法如下: drawLine(): 绘制直线drawString(): 绘制字符串drawRect(): 绘制矩形drawRoundRect(): 绘制…...

HCIA-RS实验-STP和RSTP(1)
这篇文章开始前,先简单说下这2个协议; 本文介绍了STP和RSTP的基本原理、优缺点以及应用场景。STP和RSTP都是生成树协议,主要作用于避免网络中的环路,保证数据包能够正常转发。在实际应用中,需要根据实际情况选择合适的…...

Leetcodes刷题之删除链表的倒数N个结点和删除链表的中间的结点
吾心信其可行,则移山填海之难,终有成功之日。 --孙中山 目录 🍉一.删除链表的倒数N个结点 🌻1.双指针 🍁2.求链表的长度 🌸二.删除链表的中间的结点 🍉一.删除链…...

Java-数据结构-并查集<二>
一.并查集的简单介绍 二. 并查集的主要构成和实现方式 三.HashMap模板和数组模板 由于在下文的模板基本一致,不再每次都罗列,大体的模板如下,若有错误可以在leetcode找到对应的题目解答,已经附上连接。 HashMap class UnionFi…...

JSP网上教学资源共享系统(源代码+论文)
通过网上教学资源共享系统的建设,完成了对于操作系统课程的远程化授课。可以使学生不受时间空间的限制,通过网络对于这门课程进行学习。建立起了基于B/C的网络化教学系统。本网站采用当前最流行的JSP网络编程技术,可以实现数据的高效、动态、…...

QT C++入门学习(1) QT Creator安装和使用
Qt官方下载 Qt 官网有一个专门的资源下载网站,所有的开发环境和相关工具都可以从这里下载,具体地址是:http://download.qt.io/ 进入链接后,是一个文件目录,依次进入这个路径:archive/qt/5.12/5.12.9/qt-o…...

UE动画状态机的事件触发顺序测试
正常A状态过渡到B状态的事件顺序: 整个流程为: 调用B状态的On Become Relevant事件调用B状态的On Update事件调用A状态的Left State Event事件调用B状态的Entered State Event事件调用B状态的Start Transition Event事件调用B状态的End Transition Even…...

数学建模的搜索技巧
你真的会使用“度娘”吗?是不是在查找所需要的东西的时候,搜出来的信息价值并不是很大,跟着北海老师学习,如何更高效的使用百度去查询自己想要的,有用的资料! 搜索技巧 完全匹配搜索 : 查询词的外边加上双…...

学成在线笔记+踩坑(10)——课程搜索、课程发布时同步索引库。
导航: 【黑马Java笔记踩坑汇总】JavaSEJavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线牛客面试题_java黑马笔记 目录 1 【检索模块】需求分析 1.1 全文检索介绍 1.2 业务流程 1.2.1、课程发布时索引库里新增一条记录 1.2.2、课程搜索 2 准…...

某应用虚拟化系统远程代码执行
漏洞简介 微步在线漏洞团队通过“X漏洞奖励计划”获取到瑞友天翼应用虚拟化系统远程代码执行漏洞情报(0day),攻击者可以通过该漏洞执行任意代码,导致系统被攻击与控制。瑞友天翼应用虚拟化系统是基于服务器计算架构的应用虚拟化平台,它将用户…...