目录

一、同步与异步

  • 同步与异步两个名次是用来表达任务的提交方式。

  • 根据进程和函数之间的通信机制,函数可以分为异步和同步。

同步

同步:进程调用函数后,函数执行完成之后,才会有返回值,没有执行完成之前,不会有返回值。

例子:

打开一个程序,我们等待他进行加载,期间不做别的操作,等他打开后再接着使用。

异步

异步:进程调用函数后,函数会直接返回收到(可以理解为:已收到,正在处理),等到处理完成,函数会通过回调或通知的方式,将结果发送给进程。

例子:

当我们打开一个软件,会先进入加载界面,这个时候我们不等待,在加载的时候去打开另一个软件。然后当另一个软件进入加载状态的时候第一个软件就已经加载好,这时候我们可以通过软件的变化或是提示得知第一个软件已经可以使用了。

二、阻塞与非阻塞

  • 阻塞与非阻塞是用来表达任务的执行状态

  • 根据进程等待函数调用时的状态,函数可以分为阻塞和非阻塞。

阻塞

在得到函数返回值之前,该进程处于挂起状态,不属于工作队列(可运行状态进程组成的队列),不会占用CPU资源。

非阻塞

进程调用函数之后,无论是否返回结果,进程都会继续运行,进程仍处于可运行状态。

  • 昨天学习了进程的三种状态,他们可以归类为阻塞态与非阻塞态

三、综合使用

1.同步阻塞

客户揣发送请求给服务揣,此时服务端处理任务时间很久,则客户端则被服务端堵塞了,所以客户端会一直等待服务端的响应,此时客户端不能做其他任何事,服务端也不接受其他客户揣的请求。这种通信机制比较简单租暴,但是效率不高。

举例:

一个进程运行,执行内部函数的时候进程要等待返回结果,这个时候cpu发现你现在在等待,cpu就不给你用了,让你进入阻塞状态。然后你因为在等待返回结果,没有继续运行别的函数,所以你处于同步状态。

2.同步非阻塞:

客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候虽然客户端会一直等待响应,但是服务端可以处理其他的请求,过一会回来处理原先的。这种方式很高效,一个服务端可以处理很多请求,不会在因为任务没有处理完而堵着,所以这是非阻塞的。

举例:

一个进程运行,执行内部函数的时候进程要等待返回结果,这个时候cpu发现你现在在等待,但是他没有直接不给你用,在给你用的同时他也处理别的进程的任务,这个时候就是非阻塞态。但是进程虽然用着cpu,可是因为没收到返回值代码一直停着没继续往下,所以他还是在同步态。

3.异步阻塞:

客户揣发送请求给服务端,此时服务端处理任务时间很久,但是客户端不会等待服务器响应,它可以做其他的任务,等服务器处理完毕后再把结果响应给客户端,客户端得到回调后再处理服务端的响应。这种方式可以避免客户端一直处于等待的状态,优化了用户体验,其实就是类似于网页里发起的ajax异步请求。

举例:

一个进程运行,执行内部函数的时候进程要等待返回结果,这个时候cpu发现你现在在等待,cpu就不给你用了,让你进入阻塞状态。然后这个进程不会一直等待最终的结果,他会继续运行别的函数,当结果出来的时候,通知一下这个进程就好了。因为进程停下来等待结果,所以处理异步状态。

4.异步非阻塞:

客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候的任务虽然处理时间会很久,但是客户端可以做其他的任务,因为他是异步的,可以在回调函数里处理响应;同时服务端是非阻塞的,所以服务端可以去处理其他的任务,如此,这个模式就显得非常的高效了 。

举例:

一个进程运行,执行内部函数的时候进程要等待返回结果,这个时候cpu发现你现在在等待,但是他没有直接不给你用,在给你用的同时他也处理别的进程的任务,这个时候就是非阻塞态。然后这个进程不会一直等待最终的结果,他会继续运行别的函数,当结果出来的时候,通知一下这个进程就好了。因为进程停下来等待结果,所以处理异步状态。

ps:将来使用的基本都是异步非阻塞

四、创建进程的多种方式

进程的创建

  但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式,一些操作系统只为一个应用程序设计,比如微波炉中的控制器,一旦启动微波炉,所有的进程都已经存在。

  而对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为4中形式创建新的进程:

  1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)

  2. 一个进程在运行过程中开启了子进程(如python代码创建进程等)

  3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)

  4. 一个批处理作业的初始化(只在大型机的批处理系统中应用)

  无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。 

multiprocessing模块

仔细说来,multiprocess不是一个模块而是python中一个操作、管理进程的包。 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便大家归类记忆,我将这部分大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。

第一种创建进程的方式

直接创建函数,用函数创建进程

同步状态:
import time

def task(name):
print('task is running',name)
time.sleep(3)
print('task is over',name) if __name__ == '__main__':
task('zzh') # 同步
print('主')

结果如下:

异步状态:
from multiprocessing import Process
import time def task(name):
print('task is running',name)
time.sleep(3)
print('task is over',name) if __name__ == '__main__':
# p1 = Process(target=task, args=('jason',)) # 第一种传参方式:位置参数(注意这里需要是元组)
p1 = Process(target=task, kwargs={'name':'jason123'}) # 第二种传参方式:关键字参数
p1.start() # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
print('主')

结果如下:

不同的操作系统中创建进程底层原理的区别

windows

以导入模块的形式创建进程

​ 因为是以导入模块的形式创建的,因此需要跟上面的代码一样,用上

if __name__ == '__main__':

来区分导入的文件是否为被执行文件,否则就会出现循环导入导致报错。

linux/mac

以拷贝代码的形式创建进程(就没有windows那些问题了)

第二种创建进程的方式

面向对象的方式

使用派生方法定义一个新的类,然后创建对象,对象调用方法创建进程

from multiprocessing import Process
import time class MyProcess(Process):
def __init__(self, name, age):
# 这里主要是为了传参才定义的双下init,但是super需要在上面,因为调用process中的init的时候会给对象的属性绑定一个默认值。
super().__init__()
self.name = name
self.age = age def run(self):
print('run is running', self.name, self.age)
time.sleep(3)
print('run is over', self.name, self.age) if __name__ == '__main__':
obj = MyProcess('jason', 123)
obj.start()
print('主')

五、进程间的数据隔离

同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)

from multiprocessing import Process
import time money = 1000 def task():
global money
money = 666
print('子进程的task函数查看money', money) if __name__ == '__main__':
p1 = Process(target=task)
p1.start() # 创建子进程
time.sleep(3) # 主进程代码等待3秒
print(money) # 主进程代码打印money

结果如下:

上面我们提到,在windows中创建进程是相当于导模块的操作,因此我们可以看成子进程的代码相当于在另外一个py文件中执行,虽然用上了global改变全局变量,因为跟主进程不在一个文件,可以看成产生了数据隔离。

六、进程的join方法

用上join方法后进程就会排队依次执行,变成同步状态

from multiprocessing import Process
import time def task(name, n):
print('%s is running' % name)
time.sleep(n)
print('%s is over' % name) if __name__ == '__main__':
p1 = Process(target=task, args=('jason1', 1))
p2 = Process(target=task, args=('jason2', 2))
p3 = Process(target=task, args=('jason3', 3))
# p.start() # 异步
'''主进程代码等待子进程代码运行结束再执行'''
# p.join()
# print('主')
start_time = time.time()
p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()
# p1.join()
# p2.join()
# p3.join()
print(time.time() - start_time) # 3秒多

七、IPC机制

  • IPC(Inter-Process Communication):进程间通信(消息队列)

  • 进程间通信——队列(multiprocess.Queue)

  • 创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

  • 说直观一点就是可以创建一个可以用于多进程间数据传输的队列

from multiprocessing import Queue

q = Queue(3)  # 括号内可以指定存储数据的个数
# 往消息队列中存放数据
q.put(111)
# print(q.full()) # 判断队列是否已满
q.put(222)
q.put(333)
# print(q.full()) # 判断队列是否已满
# 从消息队列中取出数据
print(q.get())
print(q.get())
# print(q.empty()) # 判断队列是否为空
print(q.get())
# print(q.empty()) # 判断队列是否为空
# print(q.get())
print(q.get_nowait()) """
full() empty() 在多进程中都不能使用!!!
""" from multiprocessing import Process, Queue def product(q):
q.put('子进程p添加的数据') def consumer(q):
print('子进程获取队列中的数据', q.get()) if __name__ == '__main__':
q = Queue()
# 主进程往队列中添加数据
# q.put('我是主进程添加的数据')
p1 = Process(target=consumer, args=(q,))
p2 = Process(target=product, args=(q,))
p1.start()
p2.start()
print('主')

八、生产者消费者模型

生产者消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

生产者

负责产生数据的'人'

消费者

负责处理数据的'人'

为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

举例:

我们去包子店买包子,老板就是生产者,客人就是消费者。在引入生产者消费者模型之前,就相当于老板生产出来的包子要直接塞客人嘴里让他吃掉,客人要一直处于吃包子的状态。这明显有问题,一方面可能客人太多老板的包子不够吃了,另一方面可能出现客人太少,老板把客人撑爆了。因此我们引入一个桌子,老板把刚蒸出来的包子放桌上,客人要吃的时候自己去桌上拿,如果没包子了就等老板做出来再拿。

九、进程对象的多种方法及拓展知识

进程对象的诸多方法

1.如何查看进程号(PID)
from multiprocessing import Process, current_process
current_process()
current_process().pid
import os
os.getpid()
os.getppid()
2.终止进程
p1.terminate()
ps:计算机操作系统都有对应的命令可以直接杀死进程
3.判断进程是否存活
p1.is_alive()
4.创建子进程
start()
5.join()

拓展

  • 当一个进程运行起来后,我们可以在cmd中,使用tasklist命令查看当前运行的进程信息。
  • PID(Process Identification)操作系统里指进程识别号,也就是进程标识符。操作系统里每打开一个程序都会创建一个进程ID,即PID。

含义

只要运行一程序,系统会自动分配一个标识。

是暂时唯一:进程中止后,这个号码就会被回收,并可能被分配给另一个新进程。

只要没有成功运行其他程序,这个PID会继续分配给当前要运行的程序。

如果成功运行一个程序,然后再运行别的程序时,系统会自动分配另一个PID。

  • taskkill,杀死进程(也就是关掉),可以使用help taskkill命令查看说明。

我们在cmd中输入:

taskkill /F /PID 进程对应的PID

就可以干掉那个PID对应的进程

十、守护进程

主进程创建守护进程

  其一:守护进程会在主进程代码执行结束后就终止

  其二:守护进程内无法再开启子进程,否则抛出异常:

AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

eg: 吴勇是张红的守护进程 一旦张红嗝屁了 吴勇立刻嗝屁

from multiprocessing import Process
import time def task(name):
print('德邦总管:%s' % name)
time.sleep(3)
print('德邦总管:%s' % name) if __name__ == '__main__':
p1 = Process(target=task, args=('大张红',))
p1.daemon = True
p1.start()
time.sleep(1)
print('恕瑞玛皇帝:小吴勇嗝屁了')

十一、僵尸进程与孤儿进程

僵尸进程

​ 进程执行完毕后并不会立刻销毁所有的数据,会有一些信息短暂保留下来

​ 比如进程号、进程执行时间、进程消耗功率等给父进程查看

ps:所有的进程都会变成僵尸进程(不过吧就是存在的时间不长,可能就几秒钟)

孤儿进程

​ 子进程正常运行,父进程意外死亡(比如我们跑到cmd中用taskkill关掉父进程),操作系统针对孤儿进程会派遣福利院管理那么这些进程将会成为孤儿进程。孤儿进程将会被init进程(进程号为1)所收养,并由init进程对他们完成状态收集工作。

十二、多进程数据错乱问题

模拟抢票软件

from multiprocessing import Process
import time
import json
import random # 查票
def search(name):
with open(r'data.json', 'r', encoding='utf8') as f:
data = json.load(f)
print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num'))) # 买票
def buy(name):
# 再次确认票
with open(r'data.json', 'r', encoding='utf8') as f:
data = json.load(f)
# 模拟网络延迟
time.sleep(random.randint(1, 3))
# 判断是否有票 有就买
if data.get('ticket_num') > 0:
data['ticket_num'] -= 1
with open(r'data.json', 'w', encoding='utf8') as f:
json.dump(data, f)
print('%s买票成功' % name)
else:
print('%s很倒霉 没有抢到票' % name) def run(name):
search(name)
buy(name) if __name__ == '__main__':
for i in range(10):
p = Process(target=run, args=('用户%s'%i, ))
p.start()

通过上面的代码,运行之后我们发现虽然设置成只有1张票,但是每个人都会显示买到票了。这个时候就出现了数据错乱。

但是有的时候又会发现,有时候又是正常的逻辑顺序进行抢票。

多进程操作数据很可能会造成数据错乱,解决方案>>>:互斥锁

互斥锁

将并发变成串行,牺牲了效率但是保障了数据的安全

十三、作业

1.将TCP服务端使用多进程实现并发效果

​ 聊天全部采用自动发送 不要用input手动输

服务端

# tcp 服务器端编程
import socket,threading,time def tcplink(sock, addr):
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Welcome!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
sock.close()
print('Connection from %s:%s closed.' % addr) # 服务器程序
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 监听端口
s.bind(('127.0.0.1', 9998))
s.listen(5)
print('Waiting for connection...') while True:
# 接受一个新连接
sock,addr = s.accept()
# 创建新线程来处理 TCP 连接
t = threading.Thread(target=tcplink, args=(sock,addr))
t.start()

客户端

# 与服务器通讯的客户端

import socket

# 客户端程序
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接
s.connect(('127.0.0.1', 9998))
# 接受欢迎消息
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:
# 发送数据
s.send(data)
print(s.recv(1024).decode('utf-8')) s.send(b'exit')
s.close()

结果展示:

2.查询IT行业可能出现的锁名称及概念

锁的概念

同步访问共享数据时,为了保证数据操作的原子性,需要使用锁进行访问控制。锁,本质上是一个访问权限的标记。

在程序的世界里,有各种形式的数据,它们以各种形式被访问和存储。CPU 寄存器组,一级、二级缓存,内存,磁盘,设备缓存,而从进程的角度来说,变量,栈,堆,数据库,还有各种各样的缓存。

存在数据的地方,就存在访问控制!

锁,可以有多种实现策略,只要满足标记修改的原子性,及标记本身的可见性。

常见的锁

共享锁,排他锁

乐观锁,悲观锁

公平锁,非公平锁

可重入锁,不可重入锁

偏向锁、轻量级锁、自旋锁、重量级锁

分布锁

3.整理理论内容 尝试编写cs架构的软件 实现数据的上传与下载

服务端

"""
编写cs架构的软件,实现客户端可以下载服务端的文件,如图片、视频、文本等
"""
import socketserver
import os
import json
import struct def base_path(*paths):
return os.path.normpath(os.path.join(__file__, '..', 'db', *paths)) class MyRequestHandler(socketserver.BaseRequestHandler):
DB_PATH = base_path() def header(self, request=False, warn=None, info=None, file_name=None, file_size=None):
# 定制头部
dic = {
'request': request,
'file_name': file_name,
'file_size': file_size,
'warn': warn,
'info': info,
}
dic_json_bytes = json.dumps(dic).encode('utf-8')
header_bytes = struct.pack('i', len(dic_json_bytes))
self.request.send(header_bytes)
self.request.send(dic_json_bytes) def verify_path(self, path_json):
# 逻辑判断
current_path = base_path(*path_json.split('/'))
print('current_path:', current_path)
if not os.path.isfile(current_path):
return
if len(self.DB_PATH) >= len(current_path):
return
return current_path def send(self, current_path):
file_size = os.path.getsize(current_path)
file_name = os.path.basename(current_path)
info = '执行命令成功! 正在为您下载文件....'
self.header(True, file_size=file_size, file_name=file_name, info=info)
with open(current_path, 'rb') as f:
for line in f:
# 发送数据
self.request.send(line) def handle(self):
print('客户端访问:', self.client_address)
while True:
try:
path_json = self.request.recv(1024).decode('utf-8')
print('path_json:', path_json)
if not path_json:
break
# 1. 定制头部 # 2. 逻辑判断
current_path = self.verify_path(path_json) if not current_path:
self.header(warn='对不起, 您的文件路径不存在!')
continue # 3. 发送数据
self.send(current_path)
except ConnectionResetError:
break
except Exception:
break
print("客户端断开连接:", self.client_address)
self.request.close() s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyRequestHandler)
s.serve_forever()

客户端

"""
编写cs架构的软件,实现客户端可以下载服务端的文件,如图片、视频、文本等
"""
import os
import json
from socket import * client = socket(AF_INET, SOCK_STREAM)
client.connect(("127.0.0.1", 8080)) def progress(percent, symbol='#', width=50):
if percent > 1:
percent = 1
show_progress = ("[%%-%ds]" % width) % (int(percent * width) * symbol)
print("\r%s %.2f%%" % (show_progress, percent * 100), end='') while True:
cmd = input("[下载: get /路径/路径../文件]请输入命令>>: ").strip()
if not cmd:
continue cmd_list = cmd.split()
if len(cmd_list) != 2:
print('对不起, 输入错误!')
continue cmd, path = cmd_list
if cmd != 'get':
print("对不起, 下载文件请执行命令get")
continue
# 1. 发送命令
client.send(path.encode('utf-8')) # 2. 解包
import struct header_bytes = client.recv(4)
json_dic_bytes_length = struct.unpack('i', header_bytes)[0]
json_dic_bytes = client.recv(json_dic_bytes_length)
dic = json.loads(json_dic_bytes.decode('utf-8'))
print(dic) # 3. 逻辑判断
request = dic['request']
if not request: # 失败
print(dic['warn'])
continue
info, file_size, file_name = dic['info'], dic['file_size'], dic['file_name'] # 接收数据
print(info)
print("start recv........")
with open(file_name, 'wb') as f:
recv_size = 0
while recv_size < file_size:
data_bytes = client.recv(1024)
f.write(data_bytes)
recv_size += len(data_bytes)
percent = recv_size / file_size
progress(percent)
print("\nend recv........") client.close()

11月18日内容总结——同步、异步与阻塞、非阻塞的概念、创建进程的多种方式及multiprocessing模块、进程间的数据隔离和IPC机制(队列)、生产者消费者模型、守护进程、僵尸进程、孤儿进程和多进程错乱问题的更多相关文章

  1. 4、网络并发编程--僵尸进程、孤儿进程、守护进程、互斥锁、消息队列、IPC机制、生产者消费者模型、线程理论与实操

    昨日内容回顾 操作系统发展史 1.穿孔卡片 CPU利用率极低 2.联机批处理系统 CPU效率有所提升 3.脱机批处理系统 CPU效率极大提升(现代计算机雏形) 多道技术(单核CPU) 串行:多个任务依 ...

  2. 进程部分(IPC机制及生产者消费者模型)和线程部分

    进程部分 一:进程间通信IPC机制:由于进程之间的内存空间是相互隔离的,所以为了进程间的通信需要一个共享的内存空间, 但是共享带来的问题是数据在写的时候就不安全了,所以需要一种机制既有能共享的内存 空 ...

  3. python多进程之IPC机制以及生产者消费者模型

    1.进程间通信(IPC机制) 第一种:管道 import subprocessres=subprocess.Popen('dir',shell=True, stdout=subprocess.PIPE ...

  4. 进程间通信IPC机制和生产者消费者模型

    1.由于进程之间内存隔离,那么要修改共享数据时可以利用IPC机制 我们利用队列去处理相应数据 #管道 #队列=管道+锁 from multiprocessing import Queue # q=Qu ...

  5. IPC机制和生产者消费者模型

    IPC机制:(解决进程间的数据隔离问题) 进程间通信:IPC(inter-Process Comminication) 创建共享的进程列队,Queue 是多进程的安全列队,可以使用Queue 实现多进 ...

  6. python 全栈开发,Day39(进程同步控制(锁,信号量,事件),进程间通信(队列,生产者消费者模型))

    昨日内容回顾 python中启动子进程并发编程并发 :多段程序看起来是同时运行的ftp 网盘不支持并发socketserver 多进程 并发异步 两个进程 分别做不同的事情 创建新进程join :阻塞 ...

  7. 5 并发编程-(进程)-队列&生产者消费者模型

    1.队列的介绍 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的 创建队列的类(底层就是以管道和锁定的方式实现 ...

  8. Git学习(二)(2015年11月18日)(2016年1月29日)

    2015年11月18日Git学习: .Shell 删除文件夹及其所有文件 rd/s/q 文件目录 ---------------当前为先创建本地Git库后与网上Git服务器关联------------ ...

  9. 2016年11月18日 星期五 --出埃及记 Exodus 20:9

    2016年11月18日 星期五 --出埃及记 Exodus 20:9 Six days you shall labor and do all your work,六日要劳碌作你一切的工,

  10. #queue队列 #生产者消费者模型

    #queue队列 #生产者消费者模型 #queue队列 #有顺序的容器 #程序解耦 #提高运行效率 #class queue.Queue(maxsize=0) #先入先出 #class queue.L ...

随机推荐

  1. jupyter初体验

    安装: 1.若是已经安装了anaconda,则通过  jupyter notebook 命令进入: 2.若是只安了python: pip3 install --upgrade pip   对pip进行 ...

  2. docker+nginx 安装部署修改资源目录配置文件和容器端口信息

    查看docker镜像 可以先查看docker下是否存在nginx镜像,使用如下这些命令查看: docker images: 列出所有镜像. docker images nginx: 列出所有nginx ...

  3. [C++] - GCC和LLVM对方法 warning: non-void function does not return a value [-Wreturn-type] 的处理差异

    最近做一个C++开源项目发现一个奇怪问题,通过clang编译链接执行程序每到有一个就崩溃了,gcc下则没有此问题. 后来通过调试,发现原因是bool返回的方法是没有return语句!问题是为啥还能通过 ...

  4. 【题解】CF1715A Crossmarket

    题面传送门 解决思路 首先,我们让 Megan 先走,因为他可以留下传送门.可以得知,不管怎么走,他到达终点所耗费的能量一定是 \(n+m-2\) . 然后,Stanley 走时就可以利用传送门.考虑 ...

  5. 嵌入式-C语言基础:malloc动态开辟内存空间

    #include<stdio.h> #include<stdlib.h> int main() { // char *p;//定义一个野指针:没有让它指向一个变量的地址 // ...

  6. Node.js的学习(一)node.js 的介绍

    一.简介 1.1.什么是 node.js ? node.js  一种 JavaScript 的运行环境,能够使得javascript能够脱离浏览器运行.以前 js 只能在浏览器基础上运行,能够操作的也 ...

  7. Go实现常用软件设计模式二:工厂模式

    目录: 举个栗子 概念介绍 使用场景 1.举个栗子 类图 ``` @startuml'https://plantuml.com/class-diagramclass Elephant { String ...

  8. Rust构建环境搭建

    ###安装涉及的概念rustup : 安装rust和管理版本的工具,当前rust尚处于发展阶段,存在三种类型的版本,稳定版.测试版.每日构建版本,使用rustup可以在这三种的版本之间切换,默认是稳定 ...

  9. 9 STL-queue

    ​ 重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦!  生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 写在前面,本篇章主要介绍S ...

  10. Linux面试题2:网络IO模型 & IO多路复用

    网络IO 先确定一下范围,我们讨论的都是网络IO,现阶段计算机早已经从CPU密集型转换成网络IO密集型,所以网络io的类型对于服务响应而言更重要. 五种IO模型 依据Unix的IO分类,网络IO分为五 ...