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

线程纵横:C++并发编程的深度解析与实践

hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之《线程纵横:C++并发编程的深度解析与实践》,在这篇文章中,你将会学习到C++新特性,并发编程,以及其如何带来的高性能的魅力,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!

希望这篇文章能对你有所帮助,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!(注:这章对于高性能服务器的架构非常重要哟!!!)

03d6d5d7168e4ccb946ff0532d6eb8b9.gif           

  前言:在当今多核处理器时代,并发编程已成为提高应用程序性能的关键。C++,这一长期以来备受青睐的语言,在C++11及以后的版本中引入了强大的线程库,为开发者提供了丰富的并发编程工具。本文将深入探讨C++线程库的奥秘,揭示并发编程的复杂性与美妙,并通过实际案例,带你领略C++并发编程的魅力。我们将一起探索线程的创建与管理,理解互斥锁、条件变量等同步机制,并学习如何利用这些工具解决实际问题。无论是初学者还是经验丰富的开发者,都能从中获得宝贵的知识和启示。让我们一起踏上这场并发编程的探索之旅吧! 

目录

 一.线程库

1.1 thread简单类的简单介绍 

1.2 线程函数 

1.3 线程函数参数

二.线程并发

2.1 原子操作 

2.2 mutex的多种互斥量

2.3 lock_guard与unique_lock

2.4 线程交互实例


 一.线程库

1.1 thread简单类的简单介绍 

C++11 之前,涉及到多线程问题,都是和平台相关的,比如 windows linux 下各有自己的接 口,这使得代码的可移植性比较差 C++11 中最重要的特性就是对线程进行支持了,使得 C++ 并行编程时不需要依赖第三方库 ,而且在原子操作中还引入了原子类的概念。要使用标准库中的 线程,必须包含< thread > 头文件。

常见线程函数简介: 

  1. std::thread

    • 用于创建和管理的线程对象。通过构造函数启动线程,并将一个可调用对象(如函数、Lambda表达式、函数对象)作为参数传递给线程。
  2. std::this_thread::get_id()

    • 返回当前线程的ID。
  3. std::this_thread::sleep_for

    • 使当前线程暂停执行指定的时间长度。
  4. std::this_thread::sleep_until

    • 使当前线程暂停执行直到指定的时间点。
  5. std::mutex

    • 提供基本的互斥锁功能,用于保护共享数据免受多线程同时访问。
  6. std::lock_guard

    • 用于管理互斥锁的RAII(Resource Acquisition Is Initialization)包装器,确保在作用域结束时会自动释放锁。
  7. std::unique_lock

    • 提供比std::lock_guard更灵活的互斥锁管理。允许手动锁定和解锁,支持条件变量,并且可以转移锁的所有权。
  8. std::condition_variable

    • 用于在多线程编程中同步操作。通常与std::mutex一起使用,用于等待某个条件成立或通知其他线程条件已经改变。
  9. std::condition_variable_any

    • std::condition_variable类似,但可以与任何类型的锁(满足基本锁概念)一起使用。
  10. std::promise

    • 用于在线程之间传递一个值。可以在线程中设置值,然后其他线程可以获取这个值。
  11. std::future

    • 用于获取std::promise设置的值。可以查询std::promise设置的结果,并检查它是否已准备好。
  12. std::packaged_task

    • 用于将任何可调用对象打包成一个可以异步执行的函数对象。与std::future结合使用,可以获取异步操作的结果。
  13. std::async

    • 用于异步执行函数或可调用对象,并返回一个std::future对象,用于获取异步操作的结果。

这些是C++线程库中一些常用的函数和类。它们提供了线程的创建、同步、通信等基本功能,是进行多线程编程的基础。

注意:
1. 线程是操作系统中的一个概念, 线程对象可以关联一个线程,用来控制线程以及获取线程的
状态
2. 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。

1.2 线程函数 

当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。
线程函数一般情况下可按照以下三种方式提供:
1) 函数指针
2) ambda表达式
3) 函数对象

         

 给大家一个简单例子:

#include <iostream>
using namespace std;
#include <thread>
void ThreadFunc(int a)
{cout << "Thread1" << a << endl;
}
class TF
{
public:void operator()(){cout << "Thread3" << endl;}
};
int main()
{// 线程函数为函数指针thread t1(ThreadFunc, 10);// 线程函数为lambda表达式thread t2([]{cout << "Thread2" << endl; });// 线程函数为函数对象TF tf;thread t3(tf);t1.join();t2.join();t3.join();cout << "Main thread!" << endl;return 0;
}
  注意:thread 类是防拷贝的,不允许拷贝构造以及赋值,但是可以移动构造和移动赋值,即将一个 线程对象关联线程的状态转移给其他线程对象,转移期间不意向线程的执行。
可以通过 jionable() 函数判断线程是否是有效的,如果是以下任意情况,则线程无效:
1)采用无参构造函数构造的线程对象
2)线程对象的状态已经转移给其他线程对象
3)线程已经调用 jion 或者 detach 结束

1.3 线程函数参数

    线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的 ,因此:即使线程参数为引用类型,在
线程中修改后也不能修改外部实参,因为 其实际引用的是线程栈中的拷贝,而不是外部实参
给大家一个具体例子体会:
#include <thread>
void ThreadFunc1(int& x)
{x += 10;
}
void ThreadFunc2(int* x)
{*x += 10;
}
int main()
{int a = 10;// 在线程函数中对a修改,不会影响外部实参,因为:线程函数参数虽然是引用方式,但其实际
引用的是线程栈中的拷贝thread t1(ThreadFunc1, a);t1.join();cout << a << endl;// 如果想要通过形参改变外部实参时,必须借助std::ref()函数thread t2(ThreadFunc1, std::ref(a);t2.join();cout << a << endl;// 地址的拷贝thread t3(ThreadFunc2, &a);t3.join();cout << a << endl;return 0;
}

   

二.线程并发

2.1 原子操作 

多线程最主要的问题是共享数据带来的问题(即线程安全)。如果共享数据都是只读的,那么没问 题,因为只读操作不会影响到数据,更不会涉及对数据的修改,所以所有线程都会获得同样的数 据。但是,当一个或多个线程要修改共享数据时,就会产生很多潜在的麻烦

 对于传统的并发,我们可以使用加锁的方式,来保护线程数据安全:

std::mutex m;
unsigned long sum = 0L;
void fun(size_t num)
{for (size_t i = 0; i < num; ++i){m.lock();sum++;m.unlock();}
}
int main()
{cout << "Before joining,sum = " << sum << std::endl;thread t1(fun, 10000000);thread t2(fun, 10000000);t1.join();t2.join();cout << "After joining,sum = " << sum << std::endl;return 0;
}

但是缺点也非常明显:只要一个线程在对sum++的时候,其它线程会被阻断,影响控制效率,而且锁控制不好,容易造成死锁。

因此 C++11 中引入了原子操作。所谓原子操作:即不可被中断的一个或一系列操作, C++11 引入
的原子操作类型,使得线程间数据的同步变得非常高效。

 这是系统自带的原子数据,可以保护指定原子数据的线程安全。

例如直接将sum改为原子数据:

#include <iostream>
using namespace std;
#include <thread>
#include <atomic>
atomic_long sum{ 0 };
void fun(size_t num)
{for (size_t i = 0; i < num; ++i)sum ++;   // 原子操作
}
int main()
{cout << "Before joining, sum = " << sum << std::endl;thread t1(fun, 1000000);thread t2(fun, 1000000);t1.join();t2.join();cout << "After joining, sum = " << sum << std::endl;return 0;
}

 更为普遍的,我们可以使用atomic模板,定义自己需要的原子类型:

atmoic<T> t;    // 声明一个类型为T的原子类型变量t
注意:原子类型通常属于 " 资源型 " 数据,多个线程只能访问单个原子类型的拷贝,因此 C++11 中,原子类型只能从其模板参数中进行构造,不允许原子类型进行拷贝构造、移动构造以及 operator= 等,为了防止意外,标准库已经将 atmoic 模板类中的拷贝构造、移动构造、赋值运算 符重载默认删除掉了。

2.2 mutex的多种互斥量

在C++11中,<mutex>头文件提供了几种不同类型的互斥量(mutex),以满足不同的同步需求。这些互斥量包括:

  1. std::mutex

    • 最基本的互斥量类型,提供基本的互斥锁功能。它用于保护共享数据,确保同一时间只有一个线程可以访问该数据。
  2. std::recursive_mutex

    • 允许同一个线程多次获得互斥锁的递归互斥量。这对于递归函数或需要多次进入临界区的代码非常有用。
  3. std::timed_mutex

    • std::mutex类似,但提供了两个额外的成员函数try_lock_fortry_lock_until,允许线程尝试在指定的时间范围内获取互斥锁。
  4. std::recursive_timed_mutex

    • 结合了std::recursive_mutexstd::timed_mutex的特性,允许递归锁定,并且可以设置超时。
注意,线程函数调用 lock() 时,可能会发生以下三种情况:
1)如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock 之前,
该线程一直拥有该锁。
2)如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住
3)如果当前互斥量被当前调用线程锁住,则会产生死锁 (deadlock)
线程函数调用 try_lock() 时,可能会发生以下三种情况:
1)如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock
释放互斥量
2)如果当前互斥量被其他线程锁住,则当前调用线程返回 false ,而并不会被阻塞掉
3)如果当前互斥量被当前调用线程锁住,则会产生死锁 (deadlock)
try_lock_for()
  接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与
std::mutex try_lock() 不同, try_lock 如果被调用时没有获得锁则直接返回
false ),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超
时(即在指定时间内还是没有获得锁),则返回 false
try_lock_until()
   接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,
如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指
定时间内还是没有获得锁),则返回 false

2.3 lock_guardunique_lock

在多线程环境下,如果想要保证某个变量的安全性,只要将其设置成对应的原子类型即可,即高
效又不容易出现死锁问题。但是有些情况下,我们可能需要保证一段代码的安全性,那么就只能
通过锁的方式来进行控制。

 如下为lock_guard的模板源码

template<class _Mutex>
class lock_guard
{
public:
// 在构造lock_gard时,_Mtx还没有被上锁explicit lock_guard(_Mutex& _Mtx): _MyMutex(_Mtx){_MyMutex.lock();}
// 在构造lock_gard时,_Mtx已经被上锁,此处不需要再上锁
lock_guard(_Mutex& _Mtx, adopt_lock_t): _MyMutex(_Mtx){}~lock_guard() _NOEXCEPT{_MyMutex.unlock();}lock_guard(const lock_guard&) = delete;lock_guard& operator=(const lock_guard&) = delete;
private:_Mutex& _MyMutex;
};
通过上述代码可以看到, lock_guard 类模板主要是通过 RAII 的方式,对其管理的互斥量进行了封 ,在需要加锁的地方,只需要用上述介绍的 任意互斥体实例化一个 lock_guard ,调用构造函数 成功上锁,出作用域前, lock_guard 对象要被销毁,调用析构函数自动解锁,可以有效避免死锁 问题。
但是缺点依然明显:太单一,用户没办法对该锁进行控制,故c++11又提供了uniqu_lock.

std::unique_lock是C++11标准库中提供的一个模板类,用于管理互斥锁(mutex)。与std::lock_guard类似,std::unique_lock也是一个RAII(Resource Acquisition Is Initialization)包装器,用于在作用域结束或异常发生时自动释放锁。不过,std::unique_lockstd::lock_guard提供了更多的灵活性和控制。

std::unique_lock的特点包括:

  1. 灵活的锁管理std::unique_lock允许你在任何时候手动锁定或解锁互斥锁,而std::lock_guard在构造时锁定,在析构时解锁,期间不能手动控制。

  2. 条件变量支持std::unique_lock可以与std::condition_variable一起使用,用于等待特定的条件成立。这在生产者-消费者模式或其他需要等待特定信号的场景中非常有用。

  3. 所有权转移std::unique_lock对象可以通过移动构造函数和移动赋值操作符进行所有权转移。这意味着锁的所有权可以从一个std::unique_lock对象转移到另一个。

  4. 锁策略std::unique_lock的构造函数接受一个策略参数,可以指定在构造时是否立即锁定互斥锁,或者是否采用延迟锁定的策略。

  5. 锁的所有权:与std::lock_guard不同,std::unique_lock可以没有锁的所有权。这意味着它可以被用来包装一个已经锁定的互斥锁,而不需要立即解锁。

#include <mutex>
#include <iostream>
#include <thread>std::mutex mtx; // 创建一个互斥锁
std::condition_variable cv; // 创建一个条件变量void print_thread_id(int id) {std::unique_lock<std::mutex> lock(mtx); // 在作用域开始时自动锁定互斥锁// 使用条件变量等待cv.wait(lock, []{ return true; }); // 此处仅为示例,实际应用中应有具体的条件判断std::cout << "Thread #" << id << '\n';// 作用域结束时,lock对象被销毁,自动解锁互斥锁
}int main() {// 假设有多个线程调用print_thread_id// 每个线程都会在print_thread_id函数中安全地访问共享资源
}

2.4 线程交互实例

#include <thread>
#include <mutex>
#include <condition_variable>
void two_thread_print()
{std::mutex mtx;condition_variable c;int n = 100;bool flag = true;thread t1([&](){int i = 0;while (i < n){unique_lock<mutex> lock(mtx);c.wait(lock, [&]()->bool{return flag; });cout << i << endl;flag = false;i += 2; // 偶数c.notify_one();}});thread t2([&](){int j = 1;while (j < n){unique_lock<mutex> lock(mtx);c.wait(lock, [&]()->bool{return !flag; });cout << j << endl;j += 2; // 奇数flag = true;c.notify_one();}});t1.join();t2.join();
}int main()
{two_thread_print();return 0;
}

这段代码展示了如何在两个线程之间交替打印奇数和偶数。这里使用了std::mutexstd::unique_lockstd::condition_variable来实现线程间的同步。

让我们逐步解释代码的工作原理:

  1. 函数two_thread_print

    • 此函数设置了一个多线程打印的情景。它创建了一个互斥锁mtx和一个条件变量c,并初始化一个整型变量n为100,以及一个布尔型变量flagtrue
  2. 线程t1

    • t1是一个Lambda表达式创建的线程,它打印偶数。
    • i初始化为0,然后进入一个循环,直到i小于n
    • 使用unique_lock<mutex>锁定互斥锁mtx,然后调用c.wait等待条件变量c的通知。
    • c.wait的第二个参数是一个Lambda表达式,它返回flag的值。这意味着t1线程将在flagtrue时继续执行。
    • t1获得通知并继续执行时,它打印当前的i值,然后将flag设置为false,表示下一个应该打印奇数。
    • i增加2,然后调用c.notify_one通知另一个线程。
  3. 线程t2

    • t2也是一个Lambda表达式创建的线程,它打印奇数。
    • j初始化为1,然后进入一个循环,直到j小于n
    • 使用unique_lock<mutex>锁定互斥锁mtx,然后调用c.wait等待条件变量c的通知。
    • c.wait的第二个参数是一个Lambda表达式,它返回!flag的值。这意味着t2线程将在flagfalse时继续执行。
    • t2获得通知并继续执行时,它打印当前的j值,然后将flag设置为true,表示下一个应该打印偶数。
    • j增加2,然后调用c.notify_one通知另一个线程。
  4. 主线程

    • 主线程创建t1t2线程,然后分别调用它们的join方法等待它们完成。
  5. 总结

    • 这个程序的关键在于flag变量和条件变量c的使用。flag用于控制哪个线程应该打印,而条件变量c用于线程间的同步。
    • 当一个线程打印一个数字并改变flag的值后,它通过notify_one通知另一个线程。
    • 另一个线程等待条件变量c的通知,并在flag的值改变后继续执行。

这个程序展示了如何在C++中使用线程和条件变量来实现复杂的线程间同步。

       好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦4d7d9707063b4d9c90ac2bca034b5705.png!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!2cd0d6ee4ef84605933ed7c04d71cfef.jpeg  

相关文章:

线程纵横:C++并发编程的深度解析与实践

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之《线程纵横&#xff1a;C并发编程的深度解析与实践》&#xff0c;在这篇文章中&#xff0c;你将会学习到C新特性&#xff0c;并发编程&#xff0c;以及其如何带来的高性能的魅力&#xff0…...

在阿里云服务器上安装MySQL

目录 一、先卸载不需要的环境 1.关闭MySQL服务 2.查看安装包以及卸载安装包 3.依次卸载所有包 4. 获取mysql官⽅yum源 二、安装&#xff08;密钥过期解决方法&#xff09; 三、启动并进入 关于MySQL MySQL是一个广泛使用的开源关系型数据库管理系统&#xff08;RDBMS&…...

国标GB28181协议EasyCVR视频汇聚平台获取设备录像仅展示部分片段的原因排查

国标GB28181协议EasyCVR安防平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流&#xf…...

Java的类和对象(一)—— 初始类和对象,this关键字,构造方法

前言 从这篇文章开始&#xff0c;我们就进入到了JavaSE的核心部分。这篇文章是Java类和对象的第一篇&#xff0c;主要介绍类和对象的概念&#xff0c;this关键字以及构造方法~~ 什么是类&#xff1f;什么是对象&#xff1f; 学过C语言的老铁们&#xff0c;可以类比struct自定义…...

富格林:曝光虚假套路规避亏损

富格林指出&#xff0c;在现货黄金市场中&#xff0c;交易时间很充足投资机会也多的是&#xff0c;但为什么还是有人亏损甚至爆仓呢&#xff1f;其实导致这种情况&#xff0c;是因为有一些投资者不知道其中的虚假套路&#xff0c;很容易就一头栽进去了。要规避虚假套路带来的亏…...

数据源网站分享

1. 国家统计局&#xff1a; http://www.stats.gov.cn/提供国家宏观经济数据 2. 工业和信息化部&#xff1a; http://www.miit.gov.cn 发布工业运行及信息化相关数据 3. 中国人民银行&#xff1a; http://www.pbc.gov.cn/ 提供金融市场政策及运行相关数据 4. 国家金融监督…...

Flutter 中的 CupertinoAlertDialog 小部件:全面指南

Flutter 中的 CupertinoAlertDialog 小部件&#xff1a;全面指南 在Flutter中&#xff0c;CupertinoAlertDialog是用于在iOS风格的应用中显示警告或提示信息的模态对话框。它以其圆角卡片和模糊背景为特点&#xff0c;为用户提供了一个简洁而直观的交互界面。CupertinoAlertDi…...

【RAG 论文】UPR:使用 LLM 来做检索后的 re-rank

论文&#xff1a;Improving Passage Retrieval with Zero-Shot Question Generation ⭐⭐⭐⭐ EMNLP 2022, arXiv:2204.07496 Code: github.com/DevSinghSachan/unsupervised-passage-reranking 论文&#xff1a;Open-source Large Language Models are Strong Zero-shot Query…...

安全风险 - 如何解决 setAccessible(true) 带来的安全风险?

可能每款成熟的金融app上架前都会经过层层安全检测才能执行上架&#xff0c;所以我隔三差五就能看到安全检测报告中提到的问题&#xff0c;根据问题的不同级别&#xff0c;处理的优先级也有所不同&#xff0c;此次讲的主要是一个 “轻度问题” &#xff0c;个人认为属于那种可改…...

创建继承自QObject的线程:一个详细指南

目录标题 步骤 1&#xff1a;创建一个新的QObject子类步骤 2&#xff1a;在新的QObject子类中实现工作代码步骤 3&#xff1a;创建一个新的QThread对象步骤 4&#xff1a;管理线程的生命周期步骤 5&#xff1a;处理线程间通信结论 在Qt中&#xff0c;线程可以通过继承QThread类…...

java项目之智慧图书管理系统设计与实现(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的智慧图书管理系统设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 智慧图书管理…...

分享一些人生道理,希望能对大家有所帮助!

1. 别总想出风头&#xff0c;炫耀就是深渊&#xff0c;贪心就是毁灭&#xff0c;人性的恶一旦被激发&#xff0c;后果不堪设想。 2. 戒取怨之言&#xff1a;不要说招人怨恨的话&#xff0c;播下使人怨恨的种子。 3. 学会感恩&#xff0c;因为感恩能够让你更加幸福。 4. 玉碎不能…...

【设计模式】JAVA Design Patterns——Abstract-document(抽象文档模式)

&#x1f50d; 目的 使用动态属性&#xff0c;并在保持类型安全的同时实现非类型化语言的灵活性。 &#x1f50d; 解释 抽象文档模式使您能够处理其他非静态属性。 此模式使用特征的概念来实现类型安全&#xff0c;并将不同类的属性分离为一组接口 真实世界例子 考虑由多个部…...

5.13网络编程

只要在一个电脑中的两个进程之间可以通过网络进行通信那么拥有公网ip的两个计算机的通信是一样的。但是一个局域网中的两台电脑上的虚拟机是不能进行通信的&#xff0c;因为这两个虚拟机在电脑中又有各自的局域网所以通信很难实现。 socket套接字是一种用于网络间进行通信的方…...

那些年使用过的UA头

一些WAF会根据扫描器UA头进行屏蔽 UA头 在sqlmap 中可以使用 –random-agnet /xx.txt 来更换UA头 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60 Opera/8.0 (Windows NT 5.1; U; en) Mozi…...

IT技术产品:开发者极为重要的思维习惯

1、特色内容预告 1、我用敏捷开发思维&#xff0c;提高工作效率。 2、我用代码批判思维&#xff0c;逐渐让自己的作品变得无可挑剔。 3、我是一个顶级程序员&#xff0c;是哪些重要的专业习惯&#xff0c;让我如此优秀&#xff1f; 2、可以免费获取到的IT资源 1、《软件工程&a…...

软件产品质量模型及其子特性

一、功能性 子特性&#xff1a; 功能的完备性 功能正确性 功能适合性 功能性的依从性 二、性能效率 子特性&#xff1a; 时间特性 资源利用性 容量 性能效率的依从性 三、兼容性 子特性&#xff1a; 共存性 互操作性 兼容性的依从性 四、易用性 子特性&#xff1a; 可辨识性…...

神经网络中的误差反向传播(Backpropagation)方法理解

想象一下&#xff0c;神经网络就像是一个复杂的迷宫&#xff0c;里面有许多交叉路口&#xff08;神经元&#xff09;&#xff0c;每个路口都有指示牌告诉你往哪个方向走&#xff08;权重&#xff09;&#xff0c;而你的目标是找到从入口到出口的最佳路径&#xff0c;使得从起点…...

Day 32 shell变量及运算

一&#xff1a;变量概述 1.什么是变量 ​ 变量来源于数学&#xff0c;是计算机语言中能储存计算结果或能表示值的抽象概念 ​ 变量可以通过变量名访问&#xff0c;在指令式语言中&#xff0c;变量通常是可变的&#xff1b;在某些条件下也是不可变的 2.变量的规则 ​ 命名只…...

八、VUE内置指令

一、初识VUE 二、再识VUE-MVVM 三、VUE数据代理 四、VUE事件处理 五、VUE计算属性 六、Vue监视属性 七、VUE过滤器 七、VUE内置指令 九、VUE组件 v-text 向其所在的节点中渲染文本内容。 (纯文本渲染)与插值语法的区别&#xff1a;v-text会替换掉节点中的内容&#xff0c;{{x…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...