当前位置: 首页 > news >正文

C++多线程并发

文章目录

  • C++多线程并发
    • std::chrono
    • C++中的多线程:std::thread
    • 主线程等待子线程结束:join
    • 主线程分离子线程:detach
    • 异步:std::async
      • 异步的另一种用法:std::launch::deferred
      • std::async的底层实现:std::promise
      • std::future的tip
    • 互斥量
      • std::mutex 上锁
      • std::lock_guard
      • std::unique_lock
      • try_lock
    • 死锁
    • 读写锁:shared_mutex
    • 条件变量(信号量)
      • 等待某个条件成真
      • 案例
    • 原子操作

C++多线程并发

std::chrono

C++11开始引入时间标准库

  • 利用C++强类型的特点,明确区分时间点和时间段,明确区分不同的时间单位。
// 时间点的例子:2022年1月8日 13点07分10秒
// 时间段的例子:1分30秒
// 时间点类型:chrono::steady_clock::time_point
// 时间段类型:chrono::milliseconds, chrono::seconds, chrono::munutes等
// 方便的元素安抚重载:时间点+时间段=时间点,时间点-时间点=时间段
auto t0 = chrono::steady_clock::now();
auto t1 = t0 + chrono::seconds(30);
auto dt = t1 - t0;
int64_t sec = chrono::duration_cast<chrnon::seconds>(dt).count() 
  • 跨平台的sleep:std::this_thread::sleep_for

可以使用std::this_thread::sleep_for替换unix的usleep。他可以让当前线程休眠一段时间,然后继续
而且单位也可以自己指定,比如milliseconds表示毫秒,也可以换成是microseconds表示微妙,seconds表示秒,chrono的强类型让单位选择更自由

 std::this_thread::sleep_for(chrono::milliseconds(1000));
  • 为什么需要多线程:无阻塞多任务

我们的程序常常需要同时处理多个任务:例如后台执行一个很耗时的任务,比如下载一个文件,同时还要和用户交互。在GUI应用程序中很常见,比如浏览器在后台下载文件的同时,用户仍然可以用鼠标操作其GUI界面

C++中的多线程:std::thread

  • C++11开始,为多线程提供了语言级别的支持。他用std::thread这个类来表示线程
  • std::thread构造函数的参数可以是任意的lambda表达式
  • 当那个线程启动的时候,就会执行这个lambda表达式里面的内容
  • 这样就可以一边和用户交互,一边在另外一个线程里面慢吞吞的下载文件了

在cmake中为了跨平台引入线程包,cmake引入了自己的线程包

find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads)

主线程等待子线程结束:join

如果我们遇到子线程还没有执行完成的时候,主线程就退出的情况,我们则不要着急推出主线程,要让主线程等待子线程结束再退出。我们可以使用std::thread()类的成员函数join()来等待进程的结束。

主线程分离子线程:detach

std::thread的析构函数会销毁线程。作为一个C++类,std::thread同样遵循着RAII思想和三五法则:因为管理着资源,他自定义了析构函数,删除了拷贝构造/赋值构造,但是提供了移动构造/赋值函数

    thread(thread&& _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}thread& operator=(thread&& _Other) noexcept {if (joinable()) {_STD terminate();}_Thr = _STD exchange(_Other._Thr, {});return *this;}thread(const thread&) = delete;thread& operator=(const thread&) = delete;void swap(thread& _Other) noexcept {_STD swap(_Thr, _Other._Thr);}

因此当thread所在的函数退出的时候,就会调用thread的析构函数,这会销毁该线程这个时候我们调thread::detach()函数,分离该线程–意味着线程的生命周期不再由std::thread对象管理,而是在线程退出以后自动销毁自己。不过进程退出的时候,线程还是会自动退出。

异步:std::async

  • std::async是一个接受带有返回值lambda自身返回一个std::future对象
  • lambda的函数体可以在另一个线程里面执行
  • 接下来你可以在main里面做一些彼得事情,你可以在线程函数内执行别的操作
  • 最后调用future的get方法,如果此时线程函数未执行完成,会等待执行完成,并且获取对应的返回值
#include <iostream>
#include <chrono>
#include <thread>
#include <future>
using namespace std;
int test_function(int a)
{std::this_thread::sleep_for(chrono::seconds(a));return a;
}
void test()
{std::cout << "test" << std::endl;
}
int main() {std::future<int> fret = std::async([&](){return test_function(10);});test();// fret.wait();int ret = fret.get();std::cout << "ret:" << ret << std::endl;return 0;
}

当然除了get会等待线程执行完毕之外,wait也可以等待线程执行完成,但是不会返回其值

只要线程没有执行完成,wait会一直等下去,而使用wait_for则可以指定一个最长的等待时间,用chrono里的类表示单位。他会返回一个std::future_status表示等待是否成功。如果超过这个等待时间线程还没有执行完毕,则放弃等待,返回future_status::timeout。
如果线程在指定时间内执行完毕,则认为等待成功,返回future_status::ready。同理还有wait_until其参数是一个时间点。

异步的另一种用法:std::launch::deferred

std::async的第一个参数可以设置成std::launch::deferred,这个时候不会创建一个线程来执行,他只会把lambda函数体内的运算推迟到future的get被调用时。这种写法,执行函数仍然在主线程中执行,他只是函数式变成范式意义上的异步,而不涉及到真正的多线程。可以用这个实现惰性求值之类的需求。

std::async的底层实现:std::promise

如果不想让std::async帮你自动创建线程,想要手动创建线程,可以使用std::promise。然后再线程返回的时候,
用set_value设置返回值。再主线程中是哟个get_future获取future对象,进一步get可以等待并且获取线程的返回值。

#include <iostream>
#include <chrono>
#include <thread>
#include <future>using namespace std;int test_function(int a)
{std::this_thread::sleep_for(chrono::seconds(a));return a;
}void test()
{std::cout << "test" << std::endl;
}int main() {std::promise<int> pret;std::thread t1([&](){auto ret = test_function(5);pret.set_value(ret);});std::future<int> fret = pret.get_future();test();int ret = fret.get();std::cout << "ret:" << ret << std::endl;t1.join();return 0;
}

std::future的tip

future为了三五法则,阐述了拷贝构造/赋值函数。如果需要浅拷贝,实现共享同一个future对象,可以使用std::shared_future
如果不需要返回值,std::async里面的lambda返回类型可以设置魏voidfuture对象的类型也设置成std::future<void>
同理有std::promise<void>,他的set_value()不接受参数,仅仅作为同步用,不传递任何实际的值

互斥量

多线程打架问题:两个线程同时往一个vector中推数据,程序崩溃了,这是一位vector不是一个线程安全的容器,多个线程同时访问同一个vector会出现数据竞争的现象。

#include <iostream>
#include <chrono>
#include <thread>
#include <future>using namespace std;vector<int> aa;int main() {std::thread t1([&](){while(true){aa.push_back(1);std::this_thread::sleep_for(std::chrono::microseconds(0));}});std::thread t2([&](){while(true){aa.push_back(2);std::this_thread::sleep_for(std::chrono::microseconds(0));}});t1.join();t2.join();return 0;
}

std::mutex 上锁

调用std::mutex的lock()时,会检测mutex是否已经上锁,如果没有锁定,则上锁,如果已经锁定,则陷入等待,直到mutex被另一个线程解锁后才再次上锁。而调用unlock则会进行解锁操作。这样就可以保证mtx.lock和mtx.unlock之间的代码段,同一时间只有一个线程在执行,从而避免数据竞争。

std::lock_guard

std::lock_guard是一个在构造的时候调用mtx.lock(),在析构的时候自动调用mtx.unlock()。从而退出函数作用域的时候能够自动解锁,避免程序员不小心忘记解锁

std::unique_lock

std::lock_guard严格在析构的时候unlock(),但是有的时候我们会希望提前unlock()。这个时候就可以std::unique_lock,它额外存储了一个flag表示是否已经释放。他会在结构的时候检测这个flag,如果没有释放,则调用unlock(),否则不调用。

然后可以直接调用unique_lock的unlock(),函数来提前解锁,但是即使忘记解锁也没有关系,退出作用域的时候他还会自动检查一遍是否需要解锁

std::unique_lock的构造函数还可以有一个额外的参数,那就是std::defer_lock

指定了这个参数的话,std::unique_lock不会在构造函数中调用mtx.lock(),需要在之后手动调用lock()函数才能上锁,好处依然是忘记unlock(),也能自动调用unlock()。

try_lock

lock()函数会等待到mutex对象到解锁状态,我们也可以使用try_lock(),在上锁失败的时候不会等待,而是直接返回false,如果上锁成功则返回true

其中try_lock_for表示等待时间,需要使用std::time_mutex ,同理还是又try_lock_until()。

std::unique_lock可以使用std::try_to_lock做参数,和无参数相比,他会调用mtx.try_lock而不是mtx.lock()。之后可以使用grd.owns_lock()判断是否上锁成功。如果使用的参数是std::adopt_lock做参数,则表示mtx是已经上锁的。

死锁

解决办法:

  • 最简单的是一个线程永远不要持有两个锁,分别上锁,同样也可以避免死锁

  • 保证双方上锁顺序一致

  • 如果没有办法保证上锁顺序一致,可以试用贴std::lock(mtx1, mtx2, …)函数,一次性地对多个mutex对象上锁。他接受任意多个mutex作为参数,并且他保证在无论任意线程中调用的顺序是否相同,都不会产生死锁问题。

  • 与std::lock_guard相对应,std::lock也有std::scoped_lock。只不过他可以同时对多个mutex上锁。std::scoped_lock(mtx1, mtx2, ...)

  • 如果是自己锁自己的话,可以使用std::recursive_mutex来保证一个线程lock多次lock同一个锁的时候不会产生死锁

读写锁:shared_mutex

条件变量(信号量)

std::condition_variable cv;

cv.wait(lock)将会让当前线程陷入等待

在其他线程中调用cv.notify_one()则会唤醒一个正在等待的线程。

可以发现std::condition_variable必须和一个std::unique_lock一起用。

int main()
{condition_variable ccv;mutex mtx;std::thread t1([&]{unique_lock lck(mtx);ccv.wait(lck);std::cout << "t1 is wait" << std::endl;});std::this_thread::sleep_for(std::chrono::milliseconds(400));std::cout << "notify......" << std::endl;ccv.notify_one();t1.join();return 0;
}

等待某个条件成真

还可以给wait指定一个额外的参数,变成wait(lck, expr) 的形式,其中expr是一个lambda表达式,只有返回值为true时才会真正的唤醒,否则继续等待。

int main()
{condition_variable ccv;mutex mtx;bool ready = false;std::thread t1([&]{unique_lock lck(mtx);ccv.wait(lck, [&](){return ready;});std::cout << "t1 is wait" << std::endl;});std::this_thread::sleep_for(std::chrono::milliseconds(400));std::cout << "notify1......" << std::endl;ccv.notify_one();ready = true;std::cout << "notify2......" << std::endl;ccv.notify_one();t1.join();return 0;
}

如果有多个等待者,可以使用notify_all()来唤醒所有的等待线程,这就是为啥wait需要一个unique_lock作为参数,因为要保证多个线程被唤醒时候,只有一个能被启动。如果不需要,在wait返回之后调用lck.unlock()即可。顺便一提,wait的过程中会暂时unlock这个锁

std::condition_variable只支持std::unique_lock作为wait参数,如果需要其他类型的mutex锁,可以使用std::condition_variable_any

还有wait_for和wait_until函数,分别接收chrono时间段和时间点作为参数

案例

生产者消费者模式

类似于消费队列

#include <string>
#include <deque>
#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <vector>
using namespace std;
int main()
{condition_variable ccv;mutex mtx;vector<int> datas;std::thread t1([&]{for (int i = 0; i < 2; i++){unique_lock lck(mtx);ccv.wait(lck, [&](){return datas.size() != 0;});auto it = datas.back();datas.pop_back();lck.unlock();std::cout << "t1 is wait " << it << std::endl;}});std::thread t2([&]{for (int i = 0; i < 2; i++){unique_lock lck(mtx);ccv.wait(lck, [&](){return datas.size() != 0;});auto it = datas.back();datas.pop_back();lck.unlock();std::cout << "t2 is wait " << it << std::endl;}});datas.push_back(1);ccv.notify_one();datas.push_back(2);ccv.notify_one();datas.push_back(3);datas.push_back(4);ccv.notify_all();t1.join();t2.join();return 0;
}

原子操作

atomic 有专门的硬件指令加持

cpu识别到该指令的时候会锁住内存总线,放弃乱序执行等优化策略(将该指令视为一个同步点,强制同步掉之前所有的内存操作),从而向你保证该操作是原子的,不会执行到中途另外一个线程插一脚进来。

对于程序员,只需要把int修改成atomic<int> 即可,不需要像mutex那样需要手动上锁解锁,因此使用起来也比较直观

int main()
{condition_variable ccv;std::atomic<int> counter = 0;std::thread t1([&]{for (int i = 0; i < 10000; i++){// counter = counter+1;counter++;}});std::thread t2([&]{for (int i = 0; i < 10000; i++){// counter = counter+1;counter++;}});t1.join();t2.join();std::cout << counter << std::endl;return 0;
}

注意:注释起来的代码不能保证执行的原子性

除了使用运算符重载,还可以使用

// fetch_add: 和+=等价
// store: 和=等价
// load: 读取对应的值
// exchange(val)会把val写入原子变量,并返回old值
// compare_exchange_strong 读取,比较是否相等,不相等则写入

相关文章:

C++多线程并发

文章目录 C多线程并发std::chronoC中的多线程&#xff1a;std::thread主线程等待子线程结束&#xff1a;join主线程分离子线程&#xff1a;detach异步&#xff1a;std::async异步的另一种用法&#xff1a;std::launch::deferredstd::async的底层实现&#xff1a;std::promisest…...

新火种AI|摊上事儿了!13名OpenAI与谷歌员工联合发声:AI失控可能导致人类灭绝...

作者&#xff1a;小岩 编辑&#xff1a;彩云 2024年&#xff0c;OpenAI的CEO Sam Altman就没有清闲过&#xff0c;他似乎一直走在解决麻烦的路上。最近&#xff0c;他的麻烦又来了。 当地时间6月4日&#xff0c;13位来自OpenAI和Google Deep Mind的现任及前任员工联合发布了…...

Web前端后端精通:深度解析与技能进阶

Web前端后端精通&#xff1a;深度解析与技能进阶 在数字时代的浪潮中&#xff0c;Web前端后端技术的精通成为了信息科技领域的核心竞争力。本文将从四个方面、五个方面、六个方面和七个方面深入探讨Web前端后端技术的精髓&#xff0c;带领读者领略这一领域的魅力与挑战。 一、…...

【C语言】09.函数递归

递归其实是⼀种解决问题的方法&#xff0c;在C语言中&#xff0c;递归就是函数自己调用自己。 一、递归的介绍 1.1递归的思想 把⼀个大型复杂问题层层转化为⼀个与原问题相似&#xff0c;但规模较小的子问题来求解&#xff1b;直到子问题不能再被拆分&#xff0c;递归就结束…...

php高级之框架源码、宏扩展原理与开发

在使用框架的时候我们经常会看到如下代码 类的方法不会显示地声明在代码里面&#xff0c;而是通过扩展的形式后续加进去&#xff0c;这么做的好处是可以降低代码的耦合度、保证源码的完整性、团队开发的时候可以分别写自己的服务去扩展类&#xff0c;减少代码冲突等等。我自己…...

(2024,示例记忆,模型记忆,遗忘,差分评估,概率评估)深度学习中的记忆:综述

Memorization in deep learning: A survey 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0 摘要 1 引言 0 摘要 深度神经网络&#xff08;DNNs&#xff09;驱动的深度学习&#xff…...

硬件产品经理

边端协调管理平台 主页一&#xff1a;模型管理1.1 边侧模型管理 二&#xff1a;配置管理2.1 终端软件配置管理 三&#xff1a;设备管理3.1 区域位置管理3.2 工控机管理&#xff08;其实就是围绕授权&#xff09;3.3 生产设备管理3.4 设备运行管理 四&#xff1a;数据服务4.1 实…...

AES加密、解密工具类

1、AES加密、解密工具类 这篇文章,主要记录一下AES加密、解密的工具类代码,在需要使用的时候,直接复制黏贴即可。 package com.gitcode.pms.common.util;import org.slf4j.Logger; import org.slf4j.LoggerFactory;import javax.crypto.Cipher; import javax.crypto.spec.…...

普通人想要自学ai,该如何入手,看完这篇你就懂了,零基础教程!

学会了AIGC之后&#xff0c;我只想说&#xff1a;无敌是多么寂寞&#xff1f; 之前我整理一篇会议记录起码要2小时。现在交给AI &#xff0c;5分钟搞定&#xff1b; 之前整理账目总是出错&#xff0c;现在利用AI财务整合器&#xff0c;轻松解决统计难题&#xff1b; 之前写个…...

Less的简单总结

Less 是一个开源的 CSS 预处理器&#xff0c;它扩展了 CSS 语言&#xff0c;增加了变量、嵌套规则、运算符、函数等特性&#xff0c;使编写 CSS 更加高效、灵活且易于维护。下面是对 "Less" 的一个总结文档&#xff1a; 简介 名称&#xff1a;Less&#xff08;通常表…...

Android:UI:Drawable:View/ImageView与Drawable

文章目录 在View/ImageVIew中显示DrawableDrawable对View的更新操作在View/ImageVIew中显示Drawable API View.setBackground(Drawable) ImageView.setImagDrawable(Drawable) 源码分析 View.mBackground在View.draw(Canvas)中绘制,调用Drawable.draw(Canvas) ImageView.m…...

网络安全实验BUAA-全套实验报告打包

下面是部分BUAA网络安全实验✅的实验内容 &#xff1a; 认识路由器、交换机。掌握路由器配置的基本指令。掌握正确配置路由器的方法&#xff0c;使网络正常工作。 本博客包括网络安全课程所有的实验报告&#xff1a;内容详细&#xff0c;一次下载打包 实验1-路由器配置实验2-AP…...

监控易监测对象及指标之:全面监控SQL Server 2008

随着企业信息化建设的不断深入&#xff0c;数据库作为存储和管理关键业务数据的核心&#xff0c;其稳定性和性能至关重要。SQL Server 2008作为一款广泛使用的关系型数据库管理系统&#xff0c;承载着众多企业的核心业务数据。 为了确保SQL Server 2008数据库的稳定运行和高效性…...

【学习记录】6.11 阅读记录

SpringBoot多环境配置详解(application-dev.yml、application-test.yml、application-prod.yml) springboot集成mybatis【使用generatorConfig.xml配置自动生成代码】 怎么快速查看自己mysql的安装位置 解决 http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd 报错...

100TOPS算力!16GB内存顶配NVIDIA Jetson Orin NX 16GB 开箱

观前提醒&#xff1a;你以为我斥资6600买了一个NX玩&#xff1f;我其实买了三个NX NVIDIA Jetson Orin NX 简介&#xff1a; NVIDIA Jetson Orin NX是NVIDIA推出的一款高性能边缘计算平台&#xff0c;其设计目标是提供卓越的计算能力以支持各种复杂的人工智能&#xff08;AI&am…...

OCP学习笔记-007 SQL语言之一:DQL

1. DQL - Data Query Language 命令行提示符修改 SQL> set time on 10:33:58 SQL> define DEFINE _DATE = "11-DEC-22" (CHAR) DEFINE _CONNECT_IDENTIFIER = "orcl" (CHAR) DEFINE _USER = "SYS" (CHAR) DEFINE _P…...

Git之解决重复输入用户名和密码(三十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…...

Python 机器学习 基础 之 【实战案例】轮船人员获救预测实战

Python 机器学习 基础 之 【实战案例】轮船人员获救预测实战 目录 Python 机器学习 基础 之 【实战案例】轮船人员获救预测实战 一、简单介绍 二、轮船人员获救预测实战 三、数据处理 1、导入数据 2、对缺失数据的列进行填充 3、属性转换,把某些列的字符串值转换为数字…...

安全相关的一些基础知识(持续更新)

目录 1. TRNG真随机数生成 2. 对称加密和非对称加密及其区别 3. Hash算法&#xff08;摘要算法&#xff09; 4. HTTPS、TLS、SSL、HTTP区别和关系 HTTPS的基本原理 5. PSS 1. TRNG真随机数生成 True Random Number Generator 在真随机数的生成里&#xff0c;把随机数的生…...

使用TensorFlow和Keras对以ResNet50模型进行微调

以下是使用ResNet50进行微调以识别特定的新东西的代码演示。将使用TensorFlow和Keras进行这个任务。 数据集下载地址&#xff0c;解压到工程里面去&#xff1a; https://www.kaggle.com/datasets/marquis03/cats-and-dogs原始代码&#xff1a; ​ from keras.applications…...

Shell脚本要点和难点以及具体应用和优缺点介绍

Shell 脚本是一种用于自动化任务和简化常见系统命令的脚本语言,通常运行在 Unix 或 Unix-like 的系统上,如 Linux 和 macOS。Shell 脚本可以直接在命令行中执行,也可以保存为文件并通过 bash、sh、zsh 等 shell 解释器来执行。 以下是一个简单的 Shell 脚本示例,它演示了如…...

移动端浏览器的扫描二维码实现(vue-qrcode-reader与jsQR方式)

1. 实现功能 类似扫一扫的功能&#xff0c;自动识别到画面中的二维码并进行识别&#xff0c;也可以选择从相册中上传。 2. 涉及到的一些插件介绍 vue-qrcode-reader 一组用于检测和解码二维码的Vue.js组件 jsQR 一个纯粹的javascript二维码阅读库&#xff0c;该库接收原始…...

android中调用onnxruntime框架

创建空白项目 安装Android Studio及创建空白项目参考&#xff1a;【安卓Java原生开发学习记录】一、安卓开发环境的搭建与HelloWorld&#xff08;详细图文解释&#xff09;_安卓原生开发-CSDN博客 切记&#xff1a;build configuration language 一定选择Groovy&#xff01;官…...

【机器学习】与【数据挖掘】技术下【C++】驱动的【嵌入式】智能系统优化

目录 一、嵌入式系统简介 二、C在嵌入式系统中的优势 三、机器学习在嵌入式系统中的挑战 四、C实现机器学习模型的基本步骤 五、实例分析&#xff1a;使用C在嵌入式系统中实现手写数字识别 1. 数据准备 2. 模型训练与压缩 3. 模型部署 六、优化与分析 1. 模型优化 模…...

Apollo9.0 PNC源码学习之Control模块(二)

前面文章&#xff1a;Apollo9.0 PNC源码学习之Control模块&#xff08;一&#xff09; 本文将对具体控制器以及原理做一个剖析 1 PID控制器 1.1 PID理论基础 如下图所示&#xff0c;PID各参数(Kp,Ki,Kd)的作用&#xff1a; 任何闭环控制系统的首要任务是要稳、准、快的响…...

直线度测量仪发展历程!

直线度测量仪的发展历程可以概括为以下几个关键阶段&#xff1a; 拉钢丝法&#xff1a; 早期直线度测量的简单直观方法&#xff0c;利用钢丝受重力自然下垂的原理来测量直线度误差。 随着机械设备的大型化和测量精度要求的提高&#xff0c;该方法逐渐无法满足要求&#xff0c;正…...

09-spring的bean创建流程(一)

文章目录 spring中bean的创建流程finishBeanFactoryInitialization(beanFactory)beanFactory.preInstantiateSingletons();getMergedLocalBeanDefinition(beanName);流程实现FactoryBean接口,里面的对象实例化过程 spring中bean的创建流程 finishBeanFactoryInitialization(be…...

spring中基于setting和构造器的注入方式

Spring中可以通过setting和构造器两种方式进行依赖注入。 1.基于setting的注入方式&#xff08;Setter Injection&#xff09;: 实现方式&#xff1a;在类中添加对应的属性以及对应的setter方法&#xff0c;在配置文件中使用<property>元素进行注入。 示例代码&#xf…...

爬虫基本原理?介绍|实现|问题解决

爬虫基本原理&#xff1a; 模拟用户行为&#xff1a; 网络爬虫&#xff08;Web Crawler&#xff09;是一种自动化的程序&#xff0c;它模拟人类用户访问网站的方式&#xff0c;通过发送HTTP/HTTPS请求到服务器以获取网页内容。 请求与响应&#xff1a; 爬虫首先构建并发送带有…...

DevOps的原理及应用详解(六)

本系列文章简介&#xff1a; 在当今快速变化的商业环境中&#xff0c;企业对于软件交付的速度、质量和安全性要求日益提高。传统的软件开发和运维模式已经难以满足这些需求&#xff0c;因此&#xff0c;DevOps&#xff08;Development和Operations的组合&#xff09;应运而生&a…...

无锡哪里做网站/seo专员是什么

胆大心细做事&#xff0c;低调谦虚做人&#xff01;转载于:https://blog.51cto.com/09112012/2051790...

龙华app网站制作/优化设计四年级上册数学答案

linux 系统则是由 cron (crond) 这个系统服务来控制的。Linux 系统上面原本就有非常多的计划性工作&#xff0c;因此这个系统服务是默认启动的。另 外, 由于使用者自己也可以设置计划任务&#xff0c;所以&#xff0c; Linux 系统也提供了使用者控制计划任务的命令 :crontab 命…...

最新流行网站开发技术/最新推广方法

前叙&#xff1a;有灵魂的程序都是每一个程序员的最终目标。TensorFlow了解下&#xff1f; 打算花几个月学机器学习&#xff0c;TensorFlow是很好的选择&#xff0c;折腾了会环境&#xff0c;略有心得分享下。 环境&#xff1a;win10 Python&#xff1a;3.6.5 TensorFlow-GPU&…...

可信的昆明网站建设/东莞市优速网络科技有限公司

pyltp的简介 语言技术平台(LTP)经过哈工大社会计算与信息检索研究中心 11 年的持续研发和推广&#xff0c; 是国内外最具影响力的中文处理基础平台。它提供的功能包括中文分词、词性标注、命名实体识别、依存句法分析、语义角色标注等。 pyltp 是 LTP 的 Python 封装&#xff0…...

重庆网站页面优化/女生seo专员很难吗为什么

《Kotlin核心编程》阅读笔记第八章 元编程程序和数据什么是元编程常见的元编程技术Kotlin的反射kotlin和Java 反射koltin的KClasskotlin的KCallable获取参数信息Kotlin 注解无处不在的注解精准控制注解位置获取注解信息第八章 元编程 Java的反射只是元编程的一种方式。 示例&a…...

自助众筹网站建设/aso优化教程

环境&#xff1a; win10 64位&#xff0c;联想工作站台式 idea 2018.3社区免费版 问题&#xff1a;alsModel.recommendForAllUsers(2).show报错如下 Exception in thread "main" org.apache.spark.SparkException: Job aborted due to stage failure: Task not se…...