Redis--12--1--分布式锁---java

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • Redis与分布式锁
  • Jedis实现
    • 1.RedisConfig
    • 2.RedisDistLock
    • 3.应用
    • 4.加上看门狗逻辑 RedisDistLockWithDog
  • redisson实现
    • 1.依赖
    • 2.代码


Redis与分布式锁

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Jedis实现

1.RedisConfig

        <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.6.3</version></dependency>

# Redis服务器地址
redis.host=127.0.0.1
# Redis服务器连接端口
redis.port=6379
# Redis服务器连接密码(默认为空)
redis.password=null
redis.timeout=30000
# 连接池最大连接数(使用负值表示没有限制)
redis.maxTotal=30
# 连接池中的最大空闲连接
redis.maxIdle=10
redis.numTestsPerEvictionRun=1024
redis.timeBetweenEvictionRunsMillis=30000
redis.minEvictableIdleTimeMillis=1800000
redis.softMinEvictableIdleTimeMillis=10000
# 连接池最大阻塞等待时间(使用负值表示没有限制)
redis.maxWaitMillis=1500
redis.testOnBorrow=true
redis.testWhileIdle=true
redis.blockWhenExhausted=false
redis.JmxEnabled=true

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;@Configuration
@PropertySource("classpath:application.properties")
public class RedisConfig {@Value("${redis.host}")private String host;@Value("${redis.port}")private int port;@Value("${redis.timeout}")private int timeout;@Value("${redis.maxIdle}")private int maxIdle;@Value("${redis.maxWaitMillis}")private int maxWaitMillis;@Value("${redis.blockWhenExhausted}")private Boolean blockWhenExhausted;@Value("${redis.JmxEnabled}")private Boolean JmxEnabled;@Beanpublic JedisPool jedisPoolFactory() {JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxIdle(maxIdle);jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);// 连接耗尽时是否阻塞, false报异常,true阻塞直到超时, 默认truejedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);// 是否启用pool的jmx管理功能, 默认truejedisPoolConfig.setJmxEnabled(JmxEnabled);JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);return jedisPool;}
}

2.RedisDistLock


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;/*** 分布式锁的实现*/
@Component
public class RedisDistLock implements Lock {private final static int LOCK_TIME = 5*1000;//失效时间private final static String RS_DISTLOCK_NS = "tdln:"; //加锁的key的前缀/*if redis.call('get',KEYS[1])==ARGV[1] thenreturn redis.call('del', KEYS[1])else return 0 end*///释放锁的时候,确保原子。lua脚本:确保  释放锁的线程就是加锁的线程,不能被线程的线程无脑调用释放private final static String RELEASE_LOCK_LUA ="if redis.call('get',KEYS[1])==ARGV[1] then\n" +"        return redis.call('del', KEYS[1])\n" +"    else return 0 end";/*保存每个线程的独有的ID值*/private ThreadLocal<String> lockerId = new ThreadLocal<>();/*解决锁的重入*/private Thread ownerThread;private String lockName = "lock";@Autowiredprivate JedisPool jedisPool;public String getLockName() {return lockName;}public void setLockName(String lockName) {this.lockName = lockName;}public Thread getOwnerThread() {return ownerThread;}public void setOwnerThread(Thread ownerThread) {//加锁成功,就会把抢到锁的线程进行保存this.ownerThread = ownerThread;}@Overridepublic void lock() { //redis的分布式锁while(!tryLock()){try {Thread.sleep(100); //每隔100ms 都会去尝试加锁} catch (InterruptedException e) {e.printStackTrace();}}}@Overridepublic void lockInterruptibly() throws InterruptedException {throw new UnsupportedOperationException("不支持可中断获取锁!");}@Overridepublic boolean tryLock() {Thread t = Thread.currentThread();if(ownerThread==t){/*说明本线程持有锁*/return true;}else if(ownerThread!=null){/*本进程里有其他线程持有分布式锁*/return false;}Jedis jedis = jedisPool.getResource();try {String id = UUID.randomUUID().toString();SetParams params = new SetParams();params.px(LOCK_TIME);params.nx();synchronized (this){/*线程们,本地抢锁*/if((ownerThread==null)&&"OK".equals(jedis.set(RS_DISTLOCK_NS+lockName,id,params))){lockerId.set(id);setOwnerThread(t);return true;}else{return false;}}} catch (Exception e) {throw new RuntimeException("分布式锁尝试加锁失败!");} finally {jedis.close();}}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {throw new UnsupportedOperationException("不支持等待尝试获取锁!");}@Overridepublic void unlock() {if(ownerThread!=Thread.currentThread()) {throw new RuntimeException("试图释放无所有权的锁!");}Jedis jedis = null;try {jedis = jedisPool.getResource();Long result = (Long)jedis.eval(RELEASE_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS+lockName),Arrays.asList(lockerId.get()));if(result.longValue()!=0L){System.out.println("Redis上的锁已释放!");}else{System.out.println("Redis上的锁释放失败!");}} catch (Exception e) {throw new RuntimeException("释放锁失败!",e);} finally {if(jedis!=null) jedis.close();lockerId.remove();setOwnerThread(null);System.out.println("本地锁所有权已释放!");}}@Overridepublic Condition newCondition() {throw new UnsupportedOperationException("不支持等待通知操作!");}}

3.应用

在这里插入图片描述

4.加上看门狗逻辑 RedisDistLockWithDog

  1. 看门狗线程启动
  2. 通过delayDog 避免无谓的轮询,减少看门狗线程的轮序次数 阻塞延迟队列
    往延迟阻塞队列中加入元素(让看门口可以在过期之前一点点的时间去做锁的续期)
  3. 续锁逻辑:判断是持有锁的线程才能续锁
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;/*** 分布式锁,附带看门狗线程的实现:加锁,保持锁1秒*/
@Component
public class RedisDistLockWithDog implements Lock {private final static int LOCK_TIME = 1*1000;private final static String LOCK_TIME_STR = String.valueOf(LOCK_TIME);private final static String RS_DISTLOCK_NS = "tdln2:";/*if redis.call('get',KEYS[1])==ARGV[1] thenreturn redis.call('del', KEYS[1])else return 0 end*/private final static String RELEASE_LOCK_LUA ="if redis.call('get',KEYS[1])==ARGV[1] then\n" +"        return redis.call('del', KEYS[1])\n" +"    else return 0 end";/*还有并发问题,考虑ThreadLocal*/private ThreadLocal<String> lockerId = new ThreadLocal<>();private Thread ownerThread;private String lockName = "lock";@Autowiredprivate JedisPool jedisPool;public String getLockName() {return lockName;}public void setLockName(String lockName) {this.lockName = lockName;}public Thread getOwnerThread() {return ownerThread;}public void setOwnerThread(Thread ownerThread) {this.ownerThread = ownerThread;}@Overridepublic void lock() {while(!tryLock()){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}@Overridepublic void lockInterruptibly() throws InterruptedException {throw new UnsupportedOperationException("不支持可中断获取锁!");}@Overridepublic boolean tryLock() {Thread t=Thread.currentThread();/*说明本线程正在持有锁*/if(ownerThread==t) {return true;}else if(ownerThread!=null){/*说明本进程中有别的线程正在持有分布式锁*/return false;}Jedis jedis = null;try {jedis = jedisPool.getResource();/*每一个锁的持有人都分配一个唯一的id,也可采用snowflake算法*/String id = UUID.randomUUID().toString();SetParams params = new SetParams();params.px(LOCK_TIME); //加锁时间1sparams.nx();synchronized (this){if ((ownerThread==null)&&"OK".equals(jedis.set(RS_DISTLOCK_NS+lockName,id,params))) {lockerId.set(id);setOwnerThread(t);if(expireThread == null){//看门狗线程启动expireThread = new Thread(new ExpireTask(),"expireThread");expireThread.setDaemon(true);expireThread.start();}//往延迟阻塞队列中加入元素(让看门口可以在过期之前一点点的时间去做锁的续期)delayDog.add(new ItemVo<>((int)LOCK_TIME,new LockItem(lockName,id)));System.out.println(Thread.currentThread().getName()+"已获得锁----");return true;}else{System.out.println(Thread.currentThread().getName()+"无法获得锁----");return false;}}} catch (Exception e) {throw new RuntimeException("分布式锁尝试加锁失败!",e);} finally {jedis.close();}}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {throw new UnsupportedOperationException("不支持等待尝试获取锁!");}@Overridepublic void unlock() {if(ownerThread!=Thread.currentThread()) {throw new RuntimeException("试图释放无所有权的锁!");}Jedis jedis = null;try {jedis = jedisPool.getResource();Long result = (Long)jedis.eval(RELEASE_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS+lockName),Arrays.asList(lockerId.get()));System.out.println(result);if(result.longValue()!=0L){System.out.println("Redis上的锁已释放!");}else{System.out.println("Redis上的锁释放失败!");}} catch (Exception e) {throw new RuntimeException("释放锁失败!",e);} finally {if(jedis!=null) jedis.close();lockerId.remove();setOwnerThread(null);}}@Overridepublic Condition newCondition() {throw new UnsupportedOperationException("不支持等待通知操作!");}/*看门狗线程*/private Thread expireThread;//通过delayDog 避免无谓的轮询,减少看门狗线程的轮序次数   阻塞延迟队列   刷1  没有刷2private static DelayQueue<ItemVo<LockItem>> delayDog = new DelayQueue<>();//续锁逻辑:判断是持有锁的线程才能续锁private final static String DELAY_LOCK_LUA ="if redis.call('get',KEYS[1])==ARGV[1] then\n" +"        return redis.call('pexpire', KEYS[1],ARGV[2])\n" +"    else return 0 end";private class ExpireTask implements Runnable{@Overridepublic void run() {System.out.println("看门狗线程已启动......");while(!Thread.currentThread().isInterrupted()) {try {LockItem lockItem = delayDog.take().getData();//只有元素快到期了才能take到  0.9sJedis jedis = null;try {jedis = jedisPool.getResource();Long result = (Long)jedis.eval(DELAY_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS+lockItem.getKey ()),Arrays.asList(lockItem.getValue(),LOCK_TIME_STR));if(result.longValue()==0L){System.out.println("Redis上的锁已释放,无需续期!");}else{delayDog.add(new ItemVo<>((int)LOCK_TIME,new LockItem(lockItem.getKey(),lockItem.getValue())));System.out.println("Redis上的锁已续期:"+LOCK_TIME);}} catch (Exception e) {throw new RuntimeException("锁续期失败!",e);} finally {if(jedis!=null) jedis.close();}} catch (InterruptedException e) {System.out.println("看门狗线程被中断");break;}}System.out.println("看门狗线程准备关闭......");}}//    @PostConstruct
//    public void initExpireThread(){
//
//    }@PreDestroypublic void closeExpireThread(){if(null!=expireThread){expireThread.interrupt();}}
}

redisson实现

  • github项目 redisson 实现分布式锁和同步器,可以直接调用

https://github.com/redisson/redisson/
在这里插入图片描述

1.依赖

       <!--引入Redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-redis</artifactId><version>1.4.2.RELEASE</version></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.3</version></dependency>

2.代码

import java.util.concurrent.TimeUnit;/*** Redisson分布式锁接口* <p>* RLock的实现有可重入非公平锁(RedissonLock)、可重入公平锁(RedissonFairLock)、联锁(RedissonMultiLock)、 红锁(RedissonRedLock)、 读锁(RedissonReadLock)、 写锁(RedissonWriteLock)等*/
public interface DistributedLock {void lock(String lockKey);void lock(String lockKey, long timeout);void lock(String lockKey, TimeUnit unit, long timeout);boolean tryLock(String lockKey, TimeUnit unit, long leaseTime);boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);boolean unlock(String lockKey);}
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Result;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;/*** Redisson分布式锁实现-使用可重入非公平锁(RedissonLock)*/
@Slf4j
@Component
public class RedisDistributedLock implements DistributedLock {@Resourceprivate RedissonClient redissonClient;public void lock(String lockKey) {RLock lock = this.redissonClient.getLock(lockKey);lock.lock();}public void lock(String lockKey, long timeout) {RLock lock = this.redissonClient.getLock(lockKey);lock.lock(timeout, TimeUnit.SECONDS);}public void lock(String lockKey, TimeUnit unit, long timeout) {RLock lock = this.redissonClient.getLock(lockKey);lock.lock(timeout, unit);}public boolean tryLock(String lockKey, TimeUnit unit, long leaseTime) {RLock lock = this.redissonClient.getLock(lockKey);try {return lock.tryLock(5L, leaseTime, unit);} catch (InterruptedException var7) {return false;}}public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime){RLock lock = redissonClient.getLock(lockKey);try {return lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {return false;}}public boolean unlock(String lockKey){RLock lock = redissonClient.getLock(lockKey);try {if (lock.isLocked()) {lock.unlock();log.info("释放锁[{}]成功",lockKey);}return true;} catch (IllegalMonitorStateException localIllegalMonitorStateException) {log.error("释放锁[{}]失败",lockKey,localIllegalMonitorStateException);return false;}}}
  private static final String CHECK_HEART_BEAT_TASK = "checkHeartbeatTask";@Autowiredprivate DistributedLock distributedLock;try {startTime = System.currentTimeMillis();boolean lock = distributedLock.tryLock(CHECK_HEART_BEAT_TASK);//log.info("检查心跳任务获取锁状态:{}", lock);if (lock){handleCheckHeartbeatTask();} else {log.info("检查心跳任务获取分布式锁失败!");}} catch (Exception e) {log.error("检查心跳任务异常中断 {}", e.getMessage());} finally {if (distributedLock.unlock(CHECK_HEART_BEAT_TASK)) {//log.info("检查心跳任务释放分布式锁!");} else {log.warn("检查心跳任务释放分布式锁失败!");}Long endTime = System.currentTimeMillis();Long costTime = endTime - startTime;if (costTime.longValue() > heartbeatCheckTime.longValue()) {log.info("检查心跳任务耗时:{}ms", costTime);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/381413.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(六)-无人机直接C2通信

目录 引言 5.4 直接C2通信 5.4.1 概述 5.4.2 A2X直接C2通信服务的授权策略 5.4.3 USS使用A2X直接C2通信服务的C2授权程序 5.4.4 直接C2通信建立程序 引言 3GPP TS 23.256 技术规范&#xff0c;主要定义了3GPP系统对无人机&#xff08;UAV&#xff09;的连接性、身份识别…

钡铼分布式I/O系统边缘计算Modbus,MQTT,OPC UA耦合器BL206

BL206系列耦合器是一个数据采集和控制系统&#xff0c;基于强大的32 位微处理器设计&#xff0c;采用Linux操作系统&#xff0c;支持Modbus&#xff0c;MQTT&#xff0c;OPC UA协议&#xff0c;可以快速接入现场PLC、DCS、PAS、MES、Ignition和SCADA以及ERP系统&#xff0c;同时…

八股文之java基础

jdk9中对字符串进行了一个什么优化&#xff1f; jdk9之前 字符串的拼接通常都是使用进行拼接 但是的实现我们是基于stringbuilder进行的 这个过程通常比较低效 包含了创建stringbuilder对象 通过append方法去将stringbuilder对象进行拼接 最后使用tostring方法去转换成最终的…

Pycharm 安装与使用

PyCharm的安装与使用 一、什么是PyCharm PyCharm是由JetBrains开发的专业Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;提供智能代码补全、语法高亮和代码导航等编辑功能。它具有强大的调试工具和内置版本控制系统支持&#xff0c;方便代码管理和协作。PyCharm…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署Hallo :针对肖像图像动画的分层音频驱动视觉合成

目录 背景介绍 训练与推理 训练 推理 开始部署 1. 把项目源码下载到本地 2. 创建 conda 环境 3. 使用 pip 安装软件包 4. 下载预训练模型 5. 准备推理数据 6. 运行推理 关于训练 为训练准备数据 训练 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞…

高职院校人工智能人才培养成果导向系统构建、实施要点与评量方法

一、引言 近年来&#xff0c;人工智能技术在全球范围内迅速发展&#xff0c;对各行各业产生了深远的影响。高职院校作为培养高技能人才的重要基地&#xff0c;肩负着培养人工智能领域专业人才的重任。为了适应社会对人工智能人才的需求&#xff0c;高职院校需要构建一套科学、…

独立游戏《星尘异变》UE5 C++程序开发日志5——实现物流系统

目录 一、进出口清单 二、路径计算 三、包裹 1.包裹的数据结构 2.包裹在场景中的运动 四、道路 1.道路的数据结构 2.道路的建造 3.道路的销毁 4.某个有道路连接的建筑被删除 作为一个工厂类模拟经营游戏&#xff0c;各个工厂之间的运输必不可少&#xff0c;本游戏采用的…

Cisco 路由重发布 —— 实现路由信息在不同路由域间的传递

一、技术背景 在实际的组网中&#xff0c;可能会遇到这样一个场景&#xff1a;在一个网络中同时存在两种或者两种以上的路由协议。例如客户的网络原先是纯 Cisco 的设备&#xff0c;使用 EIGRP 协议将网络的路由打通。但是后来网络扩容&#xff0c;增加了一批华为的设备&#…

主流大数据调度工具DolphinScheduler之数据采集

今天继续给大家分享主流大数据调度工具DolphinScheduler&#xff0c;以及数据的ETL流程。 一&#xff1a;调度工具DS 主流大数据调度工具DolphinScheduler&#xff0c; 其定位&#xff1a;解决数据处理流程中错综复杂的依赖关系 任务支持类型&#xff1a;支持传统的shell任…

人工智能增强的心电图推导的身体质量指数作为未来心脏代谢疾病预测指标| 文献-基于人工智能(AI base)医学影像研究与疾病诊断

Title 题目 Artificial intelligence-enhancedelectrocardiography derived body massindex as a predictor of futurecardiometabolic disease 人工智能增强的心电图推导的身体质量指数作为未来心脏代谢疾病预测指标 01 文献速递介绍 心电图&#xff08;ECG&#xff09;可…

ubuntu系统vscode调试c/c++大中型项目

文章目录 1. 插件2. 项目工程3. vsode的配置setting.jsonc_cpp_properties.json 4. 启动调试 前置阅读 vscode调试第一篇 1. 插件 c/c, cmake, cmake tools 2. 项目工程 对于我的项目需要用到很多的三方库&#xff0c;三方库的版本又会有很多&#xff0c;一般都是用cmake编译…

项目笔记| 基于Arduino和IR2101的无刷直流电机控制器

本文介绍如何使用 Arduino UNO 板构建无传感器无刷直流 &#xff08;BLDC&#xff09; 电机控制器或简单的 ESC&#xff08;电子速度控制器&#xff09;。 无刷直流电机有两种类型&#xff1a;有传感器和无传感器。有感无刷直流电机内置3个霍尔效应传感器&#xff0c;这些传感…

MLIR的TOY教程学习笔记

MLIR TOY Language 文章目录 MLIR TOY Language如何编译该项目ch1: MLIR 前端IR解析ch2: 定义方言和算子 (ODS)1. 定义方言2. 定义OP3. OP相关操作4. 定义OP ODS (Operation Definition Specification)1. 基本定义2. 添加文档3. 验证OP4. 新增构造函数5. 定义打印OP的格式 ch3:…

降雨量预测 | Matlab基于ARIMA-RBF降雨量预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 降雨量预测 | Matlab基于ARIMA-RBF降雨量预测 注&#xff1a;程序和数据放在一个文件夹。 程序语言为matlab&#xff0c;程序可出预测效果图&#xff0c;指标图; 代码特点&#xff1a;参数化编程、参数可方便更改、代…

浅聊 Three.js 屏幕空间反射SSR-SSRShader

浅聊 Three.js 屏幕空间反射SSR(2)-SSRShader 前置基础 渲染管线中的相机和屏幕示意图 -Z (相机朝向的方向)||| -------------- <- 屏幕/投影平面| | || | || | (f) | <- 焦距| | ||…

前端vue框架的项目文件创建及常见Vue指令运用

前言 本文介绍前端Vue框架&#xff0c;先从npm工具创建的Vue项目开始&#xff0c;对项目结构的一些文件用途进行说明&#xff0c;随后对Vue文件编写所用的两种风格&#xff08;选项式API和组合式API风格&#xff09;做了区分&#xff0c;同时对编写代码中常见的生命周期钩子函…

Pytorch使用前期准备

一、检查英伟达驱动和CUDA Toolkit是否正确安装 1.任务管理器性能选项卡中能正确显示显卡型号则表示显卡驱动正确安装 2. CUDA Toolkit会跟随pytorch自动安装 二、虚拟环境的准备 Miniconda — Anaconda documentationhttps://docs.anaconda.com/miniconda/ 1.安装anaconda或者…

腾讯元宝上线“3D角色梦工厂”:快速生成专属3D角色!

7月16日&#xff0c;腾讯旗下大模型应用“腾讯元宝”上线“3D角色梦工厂”&#xff0c;允许用户通过上传一张五官清晰的正面头像&#xff0c;并选择不同的角色模板&#xff0c;迅速生成个人3D角色&#xff01; 技术特点 “3D角色梦工厂”将大模型生成技术与3D应用相结合&#…

大模型(LLM)选择指南:AI解决方案的12个决策点

今天我们来看看国外各家领先的大型语言模型&#xff08;LLM&#xff09;&#xff0c;这些模型来自OpenAI、Google、Anthropic、Cohere、Meta、Mistral AI以及Databricks等不同的供应商。我们会根据几个关键因素来评估这些模型&#xff0c;包括性能&#xff08;涵盖价格、质量和…

NET 语言识别,语音控制操作、语音播报

System.Speech. 》》System.Speech.Synthesis; 语音播报 》》System.Speech.Recognition 语音识别 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Speech.Recog…