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

Linux下线程的互斥与同步详解

🤖个人主页:晚风相伴-CSDN博客

💖如果觉得内容对你有帮助的话,还请给博主一键三连(点赞💜、收藏🧡、关注💚)吧

🙏如果内容有误或者有写的不好的地方的话,还望指出,谢谢!!!

让我们共同进步

下一篇《生产者消费者模型》敬请期待

目录

🔥线程间互斥的相关概念

💪互斥量的接口

初始化互斥量

销毁互斥量

互斥量的加锁与解锁

🔥探究互斥量实现原理

可重入函数和线程安全 

两者的概念区分 

常见的线程不安全和安全情况

可重入与线程安全的联系与区别

☀死锁 

产生死锁的四个必要条件

避免死锁

🔥线程同步 

条件变量 

同步的概念与竞态条件

🔥条件变量接口

初始化 

销毁条件 

条件等待

唤醒等待

🔥解释pthread_cond_wait中的互斥量


🔥线程间互斥的相关概念

  • 临界资源:多线程执行流共享的资源就叫做临界资源
  • 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
  • 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源其保护作用
  • 原子性:不会被任何调度机制打断的操作,该操作只有两种状态,要么完成,要么未完成。

先来看看下面简单实现的抢票的代码

int tickets = 1000;void* getTickets(void* args){(void)args;while(true){if(tickets > 0){usleep(1000);printf("%p: %d\n", pthread_self(), tickets);tickets--;}else{break;}}return nullptr;}int main(){pthread_t t1, t2, t3;pthread_create(&t1, nullptr, getTickets, nullptr);pthread_create(&t1, nullptr, getTickets, nullptr);pthread_create(&t1, nullptr, getTickets, nullptr);pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_join(t3, nullptr);return 0;}

结果演示

💪为什么结果会出现-1呢?

原因:首先要知道一个线程什么时候被调度,调度多长时间,完全是有计算机确定的,程序员决定不了。tickets在进行减减操作时,是分三步的

①读取数据到CPU内的寄存器中

②CPU内部进行计算--

③将结果写回内存中

为了方便叙述,这里给线程编个号

一号线程来了,由于时间片很短执行到第②步就被切走了,二号线程来了,它没有被打断,所以它执行完了这三步,并且这个线程的优先级比较高,一直执行tickets--操作,直到tickets减到1停止,在执行到第①步的时候被切走了,而一号线程回来了,继续从它被打断的地方继续向后执行,也就是从第②步开始继续向后执行,在写回内存后,tickets已经减到了1,但是这个线程又把tickets修改为了999,并且这时它的时间片很长,所以这次又一直将tickets减到了1,由于判断条件tickets不为0,所以tickets继续减减操作,此时tickets减为了0,此时二号线程来了,将0读入到寄存器中进行减减操作,所以结果出现了-1,这就导致了问题的出现。

 要解决上面的问题,就需要做到以下三点:

  1. 代码必须要有互斥行为:当代码进入临界区执行时,不允许其它线程进入临界区。
  2. 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
  3. 如果线程不在临界区中执行,那么该线程不能阻止其它线程进入临界区。

要做到以上三点,就需要一把互斥锁,将临界区资源锁住,没有拿到钥匙的线程就不能访问临界区资源,这就能做到保护了临界区资源。Linux上提供的这把互斥锁叫互斥量。

💪互斥量的接口

初始化互斥量

有两种方式初始化互斥量

方法一:全局初始化分配

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

方法二:局部初始化分配

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

参数:

  • mutex:要初始化的互斥量
  • attr:nullptr

返回值:成功返回0,失败返回错误码

销毁互斥量

 int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数:

  • mutex:要销毁的互斥量

返回值:成功返回0,失败返回错误码

 销毁互斥量时需要注意

  • 使用全局初始化的互斥量不需要销毁
  • 不要销毁一个已经加锁的互斥量
  • 已经销毁的互斥量要确保后面的代码中不再有加锁的操作

互斥量的加锁与解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回错误码

 调用pthread_mutex_lock加锁时,可能会遇到以下情况:

  • 互斥量还没被加锁,处于未锁定状态,那么调用该函数会将互斥量加锁锁定。
  • 在调用该函数之前,其它线程已经申请了锁,锁定了该互斥量,或者存在其它线程同时竞争式的申请互斥量,但没有竞争到互斥量,那么调用pthread_mutex_lock就会被阻塞,等待会吃两解锁。

所以将上面的抢票代码修改如下:

int tickets = 1000; // 临界资源class ThreadData
{
public:ThreadData(string &name, pthread_mutex_t *pmtx) : _tname(name), _pmtx(pmtx){}public:string _tname;pthread_mutex_t *_pmtx;
};void *getTickets(void *args)
{ThreadData* td = (ThreadData*)args;while (true){int n = pthread_mutex_lock(td->_pmtx); // 加锁保护临界区资源assert(n == 0);if (tickets > 0){usleep(1000);printf("%s : %d\n", td->_tname.c_str(), tickets);cout << td->_tname << " : " << tickets << endl;tickets--;n = pthread_mutex_unlock(td->_pmtx);assert(n == 0);}else{n = pthread_mutex_unlock(td->_pmtx);assert(n == 0);break;}// 处理后续的动作cout << "恭喜,抢票成功" << endl;usleep(1000);}return nullptr;
}#define THREAD_NUM 5int main()
{pthread_mutex_t mtx;pthread_mutex_init(&mtx, nullptr); // 局部定义的锁进行初始化的形式pthread_t tid[THREAD_NUM];for (int i = 0; i < THREAD_NUM; i++){string name = "thread ";name += to_string(i + 1);ThreadData *td = new ThreadData(name, &mtx);pthread_create(tid + i, nullptr, getTickets, (void *)td);}for (int i = 0; i < THREAD_NUM; i++){pthread_join(tid[i], nullptr);}pthread_mutex_destroy(&mtx); // 最后将锁释放掉return 0;
}

 结果演示:

🔥探究互斥量实现原理

加锁的目的是保证操作的原子性。 从汇编的角度来看,如果只有一条汇编语句,我们就认为该汇编语句的执行是原子的, 在汇编中给我们提供了swap或者exchange指令,该指令的作用是将内存中的数据与CPU内寄存器中的数据(CPU内寄存器中的数据也叫做执行流的上下文,寄存器的空间是被所有执行流锁共享的,但是里面的数据是被某一个执行流私有的)进行交换,由于只有一条指令,所以可以保证其原子性。

解锁时会把互斥量变为1。

可重入函数和线程安全 

两者的概念区分 

线程安全:多个线程并发执行同一段代码时,不会出现不同的结果。
重入:同一个函数被不同的执行流调用,当前一个执行流还没有执行完,就有其它的执行流再次进入该函数,我们称这种情况是重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则称为不可重入函数。

常见的线程不安全和安全情况

不安全情况:

  • 不保护共享变量的函数
  • 函数状态随着被调用,状态发生变化的函数
  • 返回指向静态变量指针的函数
  • 调用线程不安全函数的函数

 安全情况:

  •  每个线程对全局变量或者静态变量只有读取权限,而没有写入权限,一般来说这些线程是安全的。
  • 类或者接口对于线程来说都是原子操作的
  • 多个线程之间的切换不会导致该接口的执行结果存在二义性

可重入与线程安全的联系与区别

联系:

  • 函数是可重入的,那就是线程安全的。
  • 函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题。
  • 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

区别:

  • 可重入函数是线程安全函数的一种
  • 线程安全不一定是可重入的,而可重入函数则一定是线程安全的
  • 如果对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个可重入函数的锁还未释放则会产生死锁,因此是不可重入的。

☀死锁 

死锁是指子在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其它进程所占用不会释放的资源而处于的一种永久等待状态。

产生死锁的四个必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用
  • 请求与保持条件:一个执行流因请求支援而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:一个执行流已获得的资源,在未使用完之前,不能被强行剥夺
  • 循环等待条件: 若干执行流之间形成一种头尾相接的循环等待资源的关系

避免死锁

  • 破坏死锁的四个必要条件
  • 加锁顺序一致
  • 避免锁未释放的场景
  • 资源一次性分配
  • 对死锁检测
  • 银行家算法

🔥线程同步 

条件变量 

当我们申请临界资源前,要先检测临界资源是否存在,做检测的本质也是在访问临界资源,所以对临界资源的检测一定是要在加锁和解锁之间的。例如一个线程访问队列时,发现队列为空,那么它只能等待,直到其它线程将一个节点添加到队列中,在检测队列是否为空时,如果该线程一直轮询检测,那么势必要频繁的申请锁和释放锁,这样太浪费资源了,那么这种情况就需要用到条件变量了。

因此条件变量可以让线程不在频繁的自己检测了,当第一次检测到条件不满足时就挂起等待,当条件满足时,再通知该线程,让它来申请资源和访问。

同步的概念与竞态条件

同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效的解决了访问临界资源的合理性问题。

竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。

🔥条件变量接口

初始化 

和互斥量那里一样分为全局初始化和局部初始化

局部初始化 

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

参数

  • cond:要初始化的条件变量
  • attr:设置为nullptr即可

返回值:成功返回0,失败返回错误码

全局初始化

pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 

销毁条件 

int pthread_cond_destroy(pthread_cond_t *cond) ;

条件等待

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数

  • cond:要在这个条件变量上等待
  • mutex:互斥量

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒一批线程
int pthread_cond_signal(pthread_cond_t *cond);//唤醒某个线程

示例代码

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
using namespace std;#define NUM 4
typedef void (*func_t)(const string name, pthread_mutex_t *pmtx, pthread_cond_t *pcond);
volatile bool quit = false;class ThreadData
{
public:ThreadData(string &name, func_t func, pthread_mutex_t *pmtx, pthread_cond_t *pcond): _name(name), _func(func), _pmtx(pmtx), _pcond(pcond){}public:string _name;func_t _func;pthread_mutex_t *_pmtx;pthread_cond_t *_pcond;
};void func1(const string name, pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{while(!quit){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond, pmtx);//线程等待cout << name << " running... -- 1" << endl;// sleep(1);pthread_mutex_unlock(pmtx);}
}void func2(const string name, pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{while(!quit){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond, pmtx);//线程等待cout << name << " running... -- 2" << endl;// sleep(1);pthread_mutex_unlock(pmtx);}
}void func3(const string name, pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{while(!quit){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond, pmtx);//线程等待cout << name << " running... -- 3" << endl;// sleep(1);pthread_mutex_unlock(pmtx);}
}void func4(const string name, pthread_mutex_t *pmtx, pthread_cond_t *pcond)
{while(!quit){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond, pmtx);//线程等待cout << name << " running... -- 4" << endl;// sleep(1);pthread_mutex_unlock(pmtx);}
}void* Entry(void* args)
{ThreadData* tmp = (ThreadData*)args;tmp->_func(tmp->_name, tmp->_pmtx, tmp->_pcond);delete tmp;return nullptr;
}int main()
{pthread_mutex_t mtx;pthread_cond_t cond;pthread_mutex_init(&mtx, nullptr);pthread_cond_init(&cond, nullptr);pthread_t tid[NUM];func_t funcs[NUM] = {func1, func2, func3, func4};for (int i = 0; i < NUM; i++){string name = "thread ";name += to_string(i + 1);ThreadData* td = new ThreadData(name, funcs[i], &mtx, &cond);pthread_create(tid + i, nullptr, Entry, (void*)td);}int cnt = 10;while(cnt){cout << "resume thread run code..." << cnt-- << endl;pthread_cond_signal(&cond);// pthread_cond_broadcast(&cond);sleep(1);}cout << "ctrl done" << endl;quit = true;pthread_cond_broadcast(&cond);for(int i = 0; i < NUM; i++){pthread_join(tid[i], nullptr);cout << "pthread: " << tid[i] << " quit" << endl; }pthread_mutex_destroy(&mtx);pthread_cond_destroy(&cond);return 0;
}

结果演示

 

按照一定的顺序执行。

🔥解释pthread_cond_wait中的互斥量

条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去也都不会满足,所以必须还要有一个线程通过某些操作来改变共享变量,使得不满足的条件变得满足,并且友好的通知在条件变量上等待的线程。但是条件不会无缘无故的满足,这必然会牵扯到共享数据的改变。共享数据属于临界资源,因此一定要用互斥锁来保护,没有互斥锁的保护就无法安全的获取和修改共享数据了。

按照上面的说法,我们转换成代码,必须先上锁,检测到条件不满足时,pthread_cond_wait会解锁,然后在条件变量上等待,直到条件满足时,pthread_cond_wait又会重新加锁。

进入pthread_cond_wait函数后,会去检测条件是否满足,如果不满足就把互斥量变为1(解锁),直到条件满足后(pthread_cond_wait返回)将互斥量恢复成原样。

条件变量的规范使用如下

//等待条件代码
pthread_mutex_lock(&mtx);
while(条件检测)pthread_cond_wait(&cond, &mtx);
//修改条件
pthread_mutex_unlock(&mtx);//条件满足,唤醒线程代码
pthread_mutex_lock(&mtx);
//设置条件满足
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mtx);

 

相关文章:

Linux下线程的互斥与同步详解

&#x1f916;个人主页&#xff1a;晚风相伴-CSDN博客 &#x1f496;如果觉得内容对你有帮助的话&#xff0c;还请给博主一键三连&#xff08;点赞&#x1f49c;、收藏&#x1f9e1;、关注&#x1f49a;&#xff09;吧 &#x1f64f;如果内容有误或者有写的不好的地方的话&…...

【栈】736. Lisp 语法解析

本文涉及知识点 栈 LeetCode736. Lisp 语法解析 给你一个类似 Lisp 语句的字符串表达式 expression&#xff0c;求出其计算结果。 表达式语法如下所示: 表达式可以为整数&#xff0c;let 表达式&#xff0c;add 表达式&#xff0c;mult 表达式&#xff0c;或赋值的变量。表达…...

什么时候用C而不用C++?

做接口只用C&#xff0c;千万别要C。C是编译器敏感的&#xff0c;一旦导出的接口里有 std::string这些东西&#xff0c;以及类&#xff0c;注定了要为各个编译器的各个版本准备独立的库。 刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C的资料从专业入门…...

unix环境编程编程扫描版:深度解析与实践指南

unix环境编程编程扫描版&#xff1a;深度解析与实践指南 在探索Unix环境编程的广阔天地时&#xff0c;我们如同行走在一条充满未知与奇遇的旅程中。本篇文章将从四个方面、五个方面、六个方面和七个方面&#xff0c;深入剖析Unix环境编程的精髓&#xff0c;帮助读者在编程的海…...

2024年6月8日 每周新增游戏

中医百科中药: 中医百科中药是一款非常强大的中药知识科普软件&#xff0c;该应用提供500多味中草药的文献资料&#xff0c;强大的搜索功能可根据功效、特点和关键词来快速查找中药&#xff0c;而且每味中药的图片、功效、主治、炮制方法等百科知识&#xff0c;可以很好的帮助你…...

AI提示词Prompts有没有好公式?( 计育韬老师高校公益巡讲答疑实录2024)

这是计育韬老师第 8 次开展面向全国高校的新媒体技术公益巡讲活动了。而在每场讲座尾声&#xff0c;互动答疑环节往往反映了高校师生当前最普遍的运营困境&#xff0c;特此计老师在现场即兴答疑之外&#xff0c;会尽量选择有较高价值的提问进行文字答疑梳理。 *本轮巡讲主题除了…...

一个 buffer 使用的负反馈实例

端到端拥塞控制其实就是负反馈的实施。典型的做法是识别到一系列标志性事件&#xff0c;比如丢包&#xff0c;时延增加等&#xff0c;然后对这些事件做反应&#xff0c;进而形成负反馈&#xff0c;但 inflight 守恒是一种完全不同的做法&#xff0c;它将负反馈平铺到了整个传输…...

小程序简单版录音机

先来看看效果 结构 先来看看页面结构 <!-- wxml --><view class"wx-container"><view id"title">录音机</view><view id"time">{{hours}}:{{minute}}:{{second}}</view><view class"btngroup"…...

苹果手机微信如何直接打印文件

在快节奏的工作和生活中&#xff0c;打印文件的需求无处不在。但你是否曾经遇到过这样的困扰&#xff1a;打印店价格高昂&#xff0c;让你望而却步&#xff1f;今天&#xff0c;我要给大家介绍一款神奇的微信小程序——琢贝云打印&#xff0c;让你的苹果手机微信直接变身移动打…...

51.线程池大小

问题 1.线程池太小会导致程序不能充分利用系统资源、容易导致饥饿。 2.线程池过大导致更多的线程上下文切换&#xff0c;占用更多的内存。 情况一&#xff1a;CPU密集型运算 应用程序是做一些数据分析&#xff0c;需要大量的使用cpu,程序代码全部都是跟cpu相关的&#xff0…...

Python | 开房门(map)

常把map称之为映射&#xff0c;就是将一个元素&#xff08;通常称之为key键&#xff09;与一个相对应的值&#xff08;通常称之为value&#xff09;关联起来 通常用**字典dict**实现了映射这种数据结构 字典也是使用{}来包裹&#xff08;set也是{}&#xff09;&#xff0c;每…...

MATLAB 函数 function

函数定义函数调用局部函数匿名函数函数句柄子函数函数文件的位置函数的文档函数的参数函数的返回值总结 在 MATLAB中&#xff0c;函数是一个执行特定任务的代码块&#xff0c;可以被重复调用。 MATLAB函数可以执行计算、数据操作、文件处理等任务&#xff0c;并且可以接收输入…...

基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道

作者&#xff1a;尹航 在前文《基于阿里云服务网格流量泳道的全链路流量管理&#xff08;一&#xff09;&#xff1a;严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理&#xff08;二&#xff09;&#xff1a;宽松模式流量泳道》中&#xff0c;我们介绍了流…...

9行超强代码用Python工具快速获取放假日期

9行超强代码用Python工具快速获取放假日期 在很多场景下,我们需要获知国内具体的节假日安排情况,而国内每一年具体的放假安排以及调休情况,都依赖于国务院发布的具体公告,如果不想自己手动整理相关数据的话,我们可以用Python来快速获取最新的放假日期. 可以通过调用公开的 API…...

Elastic Search(ES)Java 入门实操(2)搜索代码

上篇解释了 ES 的基本概念和分词器。Elastic Search &#xff08;ES&#xff09;Java 入门实操&#xff08;1&#xff09;下载安装、概念-CSDN博客 Elastic Search&#xff08;ES&#xff09;Java 入门实操&#xff08;3&#xff09;数据同步-CSDN博客 这篇主要演示 Java 整合…...

Hudi Spark Sql Procedures 回滚 Hudi 表数据

前言 因为有 Hudi Rollback 的需求,所以单独总结 Hudi Spark Sql Procedures Rollback。 版本 Hudi 0.13.0(发现有bug)、(然后升级)0.14.1Spark 3.2.3Procedures 官方文档:https://hudi.apache.org/docs/procedures 相关阅读:Hudi Spark SQL Call Procedures学习总结…...

【重学C语言】十九、SDL2 图形化编程的使用

【重学C语言】十九、SDL2 图形化编程的使用 SDL2 的第一个程序渲染器纹理渲染1. 纹理的概念2. 加载纹理3. 渲染纹理4. 纹理设置和查询5. 纹理渲染流程6. 注意事项SDL2_imageSDL2 的第一个程序 #define SDL_MAIN_HANDLED #include <SDL.h>int main(int argc, char* argv[…...

什么是电风扇行情?

“电风扇行情” 是一个金融术语&#xff0c;用于描述证券市场中价格上下波动频繁、幅度较大&#xff0c;但总体趋势不明显的市场状况。   其名称来源于电风扇的扇叶在旋转时&#xff0c;风向不断变化的特征&#xff0c;形象地比喻了市场价格频繁变动但没有明确方向的情景。 …...

pytho入门教程

文章目录 随机数据生成的方式list操作方式数据操作方式处理缺失数据数据框操作方式画图的方式 随机数据生成的方式 # 随机生成数据的方式 # 1. 随机生成10-20之间的浮点数 holdForce random.uniform(10,20) # print(holdForce)# 2.for循环输出50个数据的方式 # for i in rang…...

Elasticsearch:ES|QL 查询 TypeScript 类型(二)

在我之前的文章 “Elasticsearch&#xff1a;ES|QL 查询 TypeScript 类型&#xff08;一&#xff09;”&#xff0c;我们讲述了如何在 Nodejs 里对 ES|QL 进行查询。在今天的文章中&#xff0c;我们来使用一个完整的例子来进行详细描述。更多有关如何使用 Nodejs 来访问 Elasti…...

元音 (音标) 和元音字母的区别

元音 [音标] 和元音字母的区别 1. 音位 (phoneme)1.1. Correspondence between letters and phonemes 2. 元音 (vowel)3. 辅音 (consonant)3.1. Consonant sounds and consonant letters 4. 元音字母 (vowel letter)References 1. 音位 (phoneme) https://en.wikipedia.org/wi…...

SMS - 基于阿里云实现手机短信验证码登录(无需备案,非测试)

目录 SMS 环境调试 从阿里云云市场中购买第三方短信服务 调试短信验证码功能 实战开发 封装组件 对外接口 调用演示 SMS 环境调试 从阿里云云市场中购买第三方短信服务 a&#xff09;进入阿里云首页&#xff0c;然后从云市场中找到 “短信” &#xff08;一定要从 云…...

使用Python编写Ping监测程序

Ping是一种常用的网络诊断工具&#xff0c;它可以测试两台计算机之间的连通性&#xff1b; 如果您需要监测某个IP地址的连通情况&#xff0c;可以使用Python编写一个Ping监测程序&#xff1b; 本文将介绍如何使用Python编写Ping监测程序 首先&#xff0c;需要导入os、sys、t…...

iptables常用命令总结

1.iptables 是什么 在Linux中&#xff0c;iptables就是一款强大而灵活的防火墙工具&#xff0c;它为系统管理员提供了广泛的配置选项&#xff0c;可以有效地控制数据包的流动&#xff0c;实现网络访问的控制及安全性增强。 iptables 使用三个不同的链来允许或阻止流量&#x…...

spring 自定义注解实现

实现自定义注解&#xff0c;通常会结合AOP&#xff08;面向切面编程&#xff09;来创建一个自定义的行为。 下面创建一个名为MyCustomAnnotation的自定义注解&#xff0c;并使用AOP编写一个切面来处理这个注解。 1. 创建自定义注解&#xff1a; import java.lang.annotation…...

10.dockerfile自动构建镜像

dockerfile自动构建镜像 类似ansible剧本&#xff0c;大小几kb 手动做镜像&#xff1a;大小几百M 首先创建一个dockerfile的路径&#xff0c;便于在路径下存在多个路径每个路径下都是dockerfile命名的脚本 注释&#xff1a;文件必须为&#xff1a;dockerfile或者Dockerfile …...

python -- series和 DataFrame增删改数据

学习目标 知道df添加新列的操作 知道insert函数插入列数据 知道drop函数删除df的行或列数据 知道drop_duplicates函数对df或series进行数据去重 知道unique函数对series进行数据去重 知道apply函数的使用方法 1 DataFrame添加列 注意:本文用到的数据集在文章顶部 1.1 直…...

window.clearInterval(timer) 清除定时器

window.clearInterval(timer)是用来清除定时器的方法。在JavaScript中&#xff0c;使用定时器可以在指定的时间间隔执行一段代码。通常&#xff0c;使用setTimeout()方法可以在一定时间后执行一次代码&#xff0c;而使用setInterval()方法可以在每个时间间隔执行一次代码。 使…...

Java项目如何外发告警日志到企业微信

前言 最近领导交代了一个需求,就是有些许客户不单单满足平台告警日志外发到邮箱、短信的形式,还要以消息聊天的形式外发给企业微信。 具体操作 1、注册企业微信。 2、登录企业微信,找到应用管理,创建应用。 3、创建完之后需要记录以下图片中两个值的信息。 4、然后记录下…...

NLP--关键词

在去停用词后的文本中进行词频统计和关键词统计以及词云图显示&#xff0c;来进行文本的关键词提取&#xff0c;让人一目了然。 1.词频统计 统计文本中多次出现的词语&#xff0c;来寻找文章中的关键词&#xff0c;因为多次出现很可能就是关键内容。调用统计数量的Counter库和…...

进网站显示建设中怎么解决/长沙seo搜索

转自&#xff1a;http://www.cnblogs.com/daqiang/archive/2011/12/04/2275646.html STM32的定时器是个强大的模块&#xff0c;定时器使用的频率也是很高的&#xff0c;定时器可以做一些基本的定时&#xff0c;还可以做PWM输出或者输入捕获功能。 时钟源问题&#xff1a; 名…...

android手机开发/seo翻译

Linux平台下如何搭建Eclipse Apache Tomcat配置的Java开发环境 本文出自 “李晨光原创技术博客” 博客&#xff0c;请务必保留此出处http://chenguang.blog.51cto.com/350944/815359 转载于:https://www.cnblogs.com/chenguang/p/3742257.html...

古尔邦节网站建设/怎么发帖子做推广

题意&#xff1a;给一个无向无环图(n<1000)&#xff0c;在尽量少的节点上放灯&#xff0c;使得所有边都被照亮&#xff0c;灯可以照亮相邻的边&#xff0c;在灯数最小的前提下&#xff0c;使得被两盏灯照亮的边最多&#xff0c;输出灯数以及被两盏灯照亮的边数&#xff0c;及…...

海西州住房建设局网站/关键字排名优化公司

https://yundun.console.aliyun.com/?spm5176.200001.0.0.CZkdXg&pcas#/cas/download/2***4?regionId 点击“下载证书“按钮 安装证书 文件说明&#xff1a; 1. 证书文件2***4.pem&#xff0c;包含两段内容&#xff0c;请不要删除任何一段内容。 2. 如果是证书系统创建的…...

网站绝对路径/百度开放平台

Python调用C、C库sf2gis163.com2014年9月24日1 目标&#xff1a;Python调用C/C中的类或方法ctypes是Python扩展库&#xff0c;自2.5以后开始加入Python。Ctypes提供了C/C与Python之间交互的映射关系&#xff0c;将库映射为一个对象。用于调用C/C库。2 原理python使用c api dlop…...

做网站工作怀孕/百度用户服务中心人工24小时电话

转自百度百科 数据库防火墙 系统&#xff0c;串联部署在数据库服务器之前&#xff0c;解决数据库应用侧和运维侧两方面的问题&#xff0c;是一款基于 数据库 协议分析与控制技术的数据库安全防护系统。DBFirewall基于主动防御机制&#xff0c;实现数据库的访问行为控制、危险…...