C# 异步编程详解(Task,async/await)

文章目录

  • 1.什么是异步
  • 2.Task 产生背景
  • 3.Thread(线程) 和 Task(异步)的区别
    • 3.1 几个名词
    • 3.2 Thread 与 Task 的区别
  • 4.Task API
    • 4.1 创建和启动任务
    • 4.2 Task 等待、延续和组合
    • 4.3 task.Result
    • 4.4 Task.Delay()Thread.Sleep() 区别
  • 5.CancellationToken 和 CancellationTokenSource 取消线程
    • 5.1 CancellationToken
    • 5.2 CancellationTokenSource
    • 5.3 示例
  • 6.asyncawait
  • 7.微软案例

1.什么是异步

  同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,我们称这个方法为异步方法。
  异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、较耗时的任务设为异步执行,可以提高程序的运行效率。net4.0在ThreadPool的基础上推出了Task类,微软极力推荐使用Task来执行异步任务,现在C#类库中的异步方法基本都用到了Task;net5.0推出了async/await,让异步编程更为方便。

2.Task 产生背景

  Task出现之前,微软的多线程处理方式有:Thread→ThreadPool→委托的异步调用,虽然也可以基本业务需要的多线程场景,但它们在多个线程的等待处理方面、资源占用方面、线程延续和阻塞方面、线程的取消方面等都显得比较笨拙,在面对复杂的业务场景下,显得有点捉襟见肘了。
  ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:

  • ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
  • ThreadPool不支持线程执行的先后次序;

正是在这种背景下,Task应运而生。Task是微软在.Net 4.0时代推出来的,也是微软极力推荐的一种多线程的处理方式,Task看起来像一个Thread,实际上,它是在ThreadPool的基础上进行的封装,Task的控制和扩展性很强,在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool。以下是一个简单的任务示例:

static void Main(string[] args)
{
    Task t = new Task(() =>
    {
        Console.WriteLine("任务开始工作……");
        Thread.Sleep(5000);  //模拟工作过程
    });
    t.Start();
    t.ContinueWith(task =>
    {
        Console.WriteLine("任务完成,完成时候的状态为:");
        Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", 
                          task.IsCanceled, task.IsCompleted, task.IsFaulted);
    });
    Console.ReadKey();
}

3.Thread(线程) 和 Task(异步)的区别

3.1 几个名词

  • 1、进程(process): 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的。
  • 2、线程(thread): 线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的。多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
    • 前台线程: 前台线程是不会被立即关闭的,它的关闭只会发生在自己执行完成时,不受外在因素的影响。假如应用程序退出,造成它的前台线程终止,此时CLR仍然保持活动并运行,使应用程序能继续运行,当它的的前台线程都终止后,整个进程才会被销毁。(Thread类默认创建的是前台线程)
    • 后台线程: 后台线程是可以随时被CLR关闭而不引发异常的,也就是说当后台线程被关闭时,资源的回收是立即的,不等待的,也不考虑后台线程是否执行完成,就算是正在执行中也立即被终止。(通过线程池/Task创建的线程都是后台线程)
  • 3、同步(sync): 发出一个功能调用时,在没有得到结果之前,该调用就不返回。
  • 4、异步(async): 与同步相对,调用在发出之后,这个调用就直接返回了,所以没有返回结果。当这个调用完成后,一般通过状态、通知和回调来通知调用者。对于异步调用,调用的返回并不受调用者控制。
    通知调用者的三种方式:
    • 状态:即监听被调用者的状态(轮询),调用者需要每隔一定时间检查一次,效率会很低。
    • 通知:当被调用者执行完成后,发出通知告知调用者,无需消耗太多性能。
    • 回调:与通知类似,当被调用者执行完成后,会调用调用者提供的回调函数。
  • 5、阻塞(block): 阻塞调用是指调用结果返回(或者收到通知)之前,当前线程会被挂起,即不继续执行后续操作。简单来说,等前一件做完了才能做下一件事。
  • 6、非阻塞(non-block): 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

3.2 Thread 与 Task 的区别

Thread 类主要用于实现线程的创建以及执行。
Task 类表示以异步方式执行的单个操作。

1、Task 是基于 Thread 的,是比较高层级的封装,Task 最终还是需要 Thread 来执行
2、Task 默认使用后台线程执行,Thread 默认使用前台线程

static void Main(string[] args)
{
    Thread thread = new Thread(obj => { Thread.Sleep(3000); });
    thread.Start();
}

// 上面代码,tread为前台线程,主程序在3秒后结束。
static void Main(string[] args)
{
    Task<int> task = new Task<int>(() => 
    {
        Thread,Sleep(3000);
        return 1;
    });
    task.Start();
}

// 上面代码,task为后台线程,主程序会瞬间结束。

3、Task 可以有返回值,Thread 没有返回值

public static void Main(string[] args)
{
    Task<int> task = new Task<int>(LongRunningTask);
    task.Start();
    Console.WriteLine(task.Result);
}   

private static int LongRunningTask()
{
    Thread.Sleep(3000);
    return 1;
}

4、Task 可以执行后续操作,Thread 不能执行后续操作

4.Task API

4.1 创建和启动任务

不带返回值:

//1.  new方式实例化一个Task,需要通过Start方法启动
Task task1 = new Task(() =>
{
    Thread.Sleep(100);
    Console.WriteLine($"hello, task1的线程ID为{Thread.CurrentThread.ManagedThreadId}");
});
task1.Start();

//2.  Task.Factory.StartNew(Action action)创建和启动一个Task     
Task task2 = Task.Factory.StartNew(() =>
{
    Thread.Sleep(100);
    Console.WriteLine($"hello, task2的线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});

//3.  Task.Run(Action action)将任务放在线程池队列,返回并启动一个Task
Task task3 = Task.Run(() =>
{
    Thread.Sleep(100);
    Console.WriteLine($"hello, task3的线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});

Console.WriteLine("执行主线程!");
Console.ReadKey();

执行主线程!
hello, task1的线程ID为4
hello, task2的线程ID为6
hello, task3的线程ID为7

带返回值:

// 1.new方式实例化一个Task,需要通过Start方法启动
Task<string> task1 = new Task<string>(() =>
{
    return $"hello, task1的ID为{Thread.CurrentThread.ManagedThreadId}";
});
task1.Start();

// 2.Task.Factory.StartNew(Func func)创建和启动一个Task
Task<string> task2 =Task.Factory.StartNew<string>(() =>
{
    return $"hello, task2的ID为{ Thread.CurrentThread.ManagedThreadId}";
});

// 3.Task.Run(Func func)将任务放在线程池队列,返回并启动一个Task
Task<string> task3= Task.Run<string>(() =>
{
    return $"hello, task3的ID为{ Thread.CurrentThread.ManagedThreadId}";
});

Console.WriteLine("执行主线程!");
Console.WriteLine(task1.Result);// 注意task.Result获取结果时会阻塞UI主线程
Console.WriteLine(task2.Result);
Console.WriteLine(task3.Result);
Console.ReadKey();

执行主线程!
hello, task1的ID为4
hello, task2的ID为6
hello, task3的ID为7

4.2 Task 等待、延续和组合

  • Wait: 针对单个Task的实例,可以task1.wait进行线程等待(阻塞主线程)
  • WaitAny: 线程列表中任何一个线程执行完毕即可执行(阻塞主线程)
  • WaitAll: 线程列表中所有线程执行完毕方可执行(阻塞主线程)
  • WhenAny: 与ContinueWith配合,线程列表中任何一个执行完毕,则继续ContinueWith中的任务(开启新线程,不阻塞主线程)
  • WhenAll: 与ContinueWith配合,线程列表中所有线程执行完毕,则继续ContinueWith中的任务(开启新线程,不阻塞主线程)
  • ContinueWith: 与WhenAny或WhenAll配合使用
  • ContinueWhenAny: 等价于Task的WhenAny+ContinueWith
  • ContinueWhenAll: 等价于Task的WhenAll+ContinueWith
//创建一个任务
Task<int> task = Task.Run<int>(() => 
{
    int sum = 0;
    Console.WriteLine("使用`Task`执行异步操作.");
    for (int i = 0; i < 1000; i++)
    {
        sum += i;
    }
    return sum;
});

Console.WriteLine("主线程执行其他处理");
//任务完成时执行处理。
Task cwt = task.ContinueWith(t =>
{
    Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
});

task.Wait();
cwt.Wait();

Action<string,int> log = (name,time) =>
{
    Console.WriteLine($"{name}任务开始...");
    Thread.Sleep(time);
    Console.WriteLine($"{name}任务结束!");
};
List<Task> tasks = new List<Task>
{
    Task.Run(() => log("张三",3000)),
    Task.Run(() => log("李四",1000)),
    Task.Run(() => log("王五",2000))
};
//以下语句逐个测试效果
Task.WaitAny(tasks.ToArray());
Task.WaitAll(tasks.ToArray());
Task.WhenAny(tasks.ToArray()).ContinueWith(x => Console.WriteLine("某个Task执行完毕"));
Task.WhenAll(tasks.ToArray()).ContinueWith(x => Console.WriteLine("所有Task执行完毕"));
Task.Factory.ContinueWhenAny(tasks.ToArray(), x => Console.WriteLine("某个Task执行完毕"));
Task.Factory.ContinueWhenAll(tasks.ToArray(), x => Console.WriteLine("所有Task执行完毕"));

Console.ReadKey();

4.3 task.Result

等待获取task返回值,阻塞调用其他线程,直到当前异步操作完成,相当于调用wait方法

static void Main(string[] args)
{
    Task<string> task = Task.Run<string>(() => 
    {
        Thread.Sleep(3000);
        return "ming_堵塞线程";
    });

    Console.WriteLine(task.Result);
    Console.WriteLine("主线程执行");

    Console.ReadKey();
}

ming_堵塞线程
主线程执行

4.4 Task.Delay()Thread.Sleep() 区别

  • Thread.Sleep()是同步延迟, Task.Delay()是异步延迟。
  • Thread.Sleep()会阻塞线程, Task.Delay()不会。
  • Thread.Sleep()不能取消, Task.Delay()可以。
  • Task.Delay()Thread.Sleep()最大的区别是Task.Delay()旨在异步运行,在同步代码中使用Task.Delay()是没有意义的;在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()
// 阻塞,出现CPU等待...
static void Main(string[] args)
{
    // 阻塞,出现CPU等待...
    Task.Factory.StartNew(() =>
    {
        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ****** Start Sleep()******");
        for (int i = 1; i <=10; i++)
        {
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "******Sleep******==>" + i);
            Thread.Sleep(1000);//同步延迟,阻塞一秒
        }
        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ******End Sleep()******");
        Console.WriteLine();
    });

    // 不阻塞
    Task.Factory.StartNew(() =>
    {
        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ======StartDelay()======");
        for (int i =1; i <=10; i++)
        {
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ======Delay====== ==>" + i);
            Task.Delay(1000);//异步延迟
        }
        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ======End Delay()======");
        Console.WriteLine();
    });

    // 不阻塞等待三秒
    Task.Factory.StartNew(async() =>
    {
        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ======StartDelay()======");
        for (int i =1; i <=10; i++)
        {
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ======Await Delay====== ==>" + i);
            await Task.Delay(1000);//异步延迟
        }
        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ======End Delay()======");
        Console.WriteLine();
    });

    Console.ReadKey();
}

5.CancellationToken 和 CancellationTokenSource 取消线程

5.1 CancellationToken

属性:

//表示当前CancellationToken是否可以被取消
public bool CanBeCanceled { get; }
//表示当前CancellationToken是否已经是取消状态
public bool IsCancellationRequested { get; }

方法:

//往CancellationToken中注册回调
public CancellationTokenRegistration Register(Action callback);
//当CancellationToken处于取消状态时,抛出System.OperationCanceledException异常
public void ThrowIfCancellationRequested();

5.2 CancellationTokenSource

属性:

//表示Token是否已处于取消状态
public bool IsCancellationRequested { get; }
//CancellationToken 对象
public CancellationToken Token { get; }

方法:

//立刻取消
public void Cancel();
//立刻取消
public void Cancel(bool throwOnFirstException);
//延迟指定时间后取消
public void CancelAfter(int millisecondsDelay);
//延迟指定时间后取消
public void CancelAfter(TimeSpan delay);

5.3 示例

CancellationTokenSource source = new CancellationTokenSource();
//注册一个线程取消后执行的逻辑
source.Token.Register(() =>
{
    //这里执行线程被取消后的业务逻辑.
    Console.WriteLine("-------------我是线程被取消后的业务逻辑---------------------");
});

Task.Run(() =>
{
    while (!source.IsCancellationRequested)
    {
        Thread.Sleep(100);
        Console.WriteLine("当前thread={0} 正在运行", Thread.CurrentThread.ManagedThreadId);
    }
}, source.Token);

Thread.Sleep(2000);
source.Cancel();

当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
当前thread=4 正在运行
-------------我是线程被取消后的业务逻辑---------------------
当前thread=4 正在运行

6.asyncawait

async:

  • async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。异步方法名字后习惯加个Async后缀
  • async 关键字修饰的方法一般包含一个或多个await 表达式或语句,如果不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法。
  • async方法可以是下面三种返回类型:
    • Task
    • Task< TResult >
    • void 这种返回类型一般用在event事件处理器中,或者用在你只需要任务执行,不关心任务执行结果的情况当中。
    • 任何其他具有GetAwaiter方法的类型(从C#7.0开始)

await:

  • await关键字只能在async 关键字修饰的方法(异步方法)中使用。
  • await 运算符的操作数通常是以下其中一个 .NET 类型:Task、Task、ValueTask 或 ValueTask。 但是,任何可等待表达式都可以是 await 运算符的操作数。

示例:
无返回值:

static void Main(string[] args)
{
    Console.WriteLine("主线程--开始");
    var task = TestTaskAsync();
    task.ContinueWith(t => Console.WriteLine("TestTaskAsync方法结束后执行"));
    Console.WriteLine("主线程--结束");

    Console.ReadKey();
}

private static async Task TestTaskAsync()
{
    Console.WriteLine("开始执行TestTaskAsync方法");
    Task task = new Task(() =>
    {
        Console.WriteLine("开始子线程耗时操作");
        Thread.Sleep(4000);
        Console.WriteLine("结束子线程耗时操作");
    });
    task.Start();
    await task;
    Console.WriteLine("await关键字后面的内容 1");
}

带返回值:

// 方法一:使用ContinueWith
Task<int> task = TestTaskIntAsync();
task.ContinueWith((t) =>
{
    COnsole.WriteLine($"TestTaskIntAsync的返回值是:{t.Result.ToString()}");
});
// 方法二:使用await
Task<int> task = TestTaskIntAsync();
int result = await task;
Console.WriteLine($"TestTaskIntAsync的返回值是:{result }");

7.微软案例

以微软文档的做早餐的案例加以简化来讲解
1.同步执行

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            PourOJ();
            PourCoffee();
            ToastBread();
            FryBacon();
            FryEggs();
            Console.WriteLine("早餐已经做完!");
            stopwatch.Stop();
            Console.WriteLine($"做早餐总计耗时:{stopwatch.ElapsedMilliseconds}");
            Console.ReadLine();
        }

        //倒橙汁
        private static void PourOJ()
        {
            Thread.Sleep(1000);
            Console.WriteLine("倒一杯橙汁");
        }

        //烤面包
        private static void ToastBread()
        {
            Console.WriteLine("开始烤面包");
            Thread.Sleep(3000);
            Console.WriteLine("烤面包好了");

        }

        //煎培根
        private static void FryBacon()
        {
            Console.WriteLine("开始煎培根");
            Thread.Sleep(6000);
            Console.WriteLine("培根煎好了");
        }
        //煎鸡蛋
        private static void FryEggs()
        {
            Console.WriteLine("开始煎鸡蛋");
            Thread.Sleep(6000);
            Console.WriteLine("鸡蛋好了");
        }

        //倒咖啡
        private static void PourCoffee()
        {
            Thread.Sleep(1000);
            Console.WriteLine("倒咖啡");
        }
    }
}

在这里插入图片描述
2.并行执行
如果此时我们每一项任务都有一个单独的人去完成
那么可以如下:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Test();
            Console.ReadLine();
        }

        private static void Test()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            List<Task> tasks = new List<Task>() { PourOJ(), ToastBread(), FryBacon(), FryEggs(), PourCoffee() };
            Task.WhenAll(tasks).ContinueWith((t)=> 
            {
                Console.WriteLine("早餐已经做完!");
                stopwatch.Stop();
                Console.WriteLine($"做早餐总计耗时:{stopwatch.ElapsedMilliseconds}");
            });
        }

        //倒橙汁
        private static async Task PourOJ()
        {
            await Task.Delay(1000);
            Console.WriteLine("倒一杯橙汁");
        }

        //烤面包
        private static async Task ToastBread()
        {
            Console.WriteLine("开始烤面包");
            await Task.Delay(3000);
            Console.WriteLine("烤面包好了");

        }

        //煎培根
        private static async Task FryBacon()
        {
            Console.WriteLine("开始煎培根");
            await Task.Delay(6000);
            Console.WriteLine("培根煎好了");
        }
        //煎鸡蛋
        private static async Task FryEggs()
        {
            Console.WriteLine("开始煎鸡蛋");
            await Task.Delay(6000);
            Console.WriteLine("鸡蛋好了");
        }

        //倒咖啡
        private static async Task PourCoffee()
        {
            await Task.Delay(1000);
            Console.WriteLine("倒咖啡");
        }
    }
}

在这里插入图片描述
3.并行且可指定顺序执行
现在呢,有个问题,不可能每次做早餐你都有那么多帮手,同时帮你,如果现在要求,先倒橙汁,然后倒咖啡,其余的操作并行执行,应该如何操作呢?
只需将以上案例的Test 方法修改如下:

private static async void Test()
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    await PourOJ();
    await PourCoffee();            
    List<Task> tasks = new List<Task>() { ToastBread(), FryBacon(), FryEggs() };
    await Task.WhenAll(tasks);
    Console.WriteLine("早餐已经做完!");
    stopwatch.Stop();
    Console.WriteLine($"做早餐总计耗时:{stopwatch.ElapsedMilliseconds}");
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux-笔记 使用SCP命令传输文件报错 :IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

前言 使用scp命令向开发板传输文件发生报错&#xff0c;报错见下图; 解决 rm -rf /home/<用户名>/.ssh/known_hosts 此方法同样适用于使用ssh命令连接开发板报错的情况。 参考 https://blog.csdn.net/westsource/article/details/6636096

VBA学习(17):使用条件格式制作Excel聚光灯

今天给大家分享的表格小技巧是制作聚光灯。 先说一下啥是聚光灯。所谓聚光灯&#xff0c;可以简单理解为对工作表的行列填充颜色&#xff0c;突出显示被选中的单元格&#xff0c;仿佛该单元格被聚光灯照亮似的。聚光灯有助于肉眼识别所选中的单元格或区域&#xff0c;提高数据…

Zookeeper部署

Zookeeper部署 下载安装包Linux解压安装包修改配置文件编辑zoo.cf配置 启动服务停止服务常用zookeeper指令查看namespace列表创建namespace删除namespace 注意&#xff1a;该文章为简单部署操作&#xff0c;没有复杂的配置内容&#xff0c;用的是3.7.2版本。 下载安装包 进入z…

实践案例:使用Jetpack Navigation创建一个新闻应用

在这个实践案例中&#xff0c;我们将使用Jetpack Navigation创建一个简单的新闻应用。这个应用将包含以下功能&#xff1a; 新闻列表页面&#xff1a;显示一组新闻文章。新闻详情页面&#xff1a;显示选定新闻文章的详细信息。用户资料页面&#xff1a;显示用户的资料信息。 …

内网部署Prometheus-server结合grafana

目录 1.提取依赖包 2.所有节点安装chrony服务 2.1集群角色规划 2.2 所有节点安装chrony服务 2.3添加hosts文件解析(服务端) 2.4修改时间服务器 ​编辑 2.5重启chrony服务&#xff0c;使得配置生效 2.6修改客户端配置&#xff08;注意是客户端其他节点&#xff09; 2.…

【Knowledge Graph Context-Enhanced Diversified Recommendation(MSDM2024)】

Knowledge Graph Context-Enhanced Diversified Recommendation 摘要 推荐系统&#xff08;RecSys&#xff09;领域已被广泛研究&#xff0c;以通过利用用户的历史交互来提高准确性。 尽管如此&#xff0c;这种对准确性的持续追求常常导致多样性的减少&#xff0c;最终导致众所…

Golang | Leetcode Golang题解之第187题重复的DNA序列

题目&#xff1a; 题解&#xff1a; const L 10 var bin map[byte]int{A: 0, C: 1, G: 2, T: 3}func findRepeatedDnaSequences(s string) (ans []string) {n : len(s)if n < L {return}x : 0for _, ch : range s[:L-1] {x x<<2 | bin[byte(ch)]}cnt : map[int]in…

ollama,springAi实现自然语言处理

ollama安装使用&#xff1a; https://ollama.com/ 下载速度比较慢的可以直接使用以下版本0.1.41 https://pan.baidu.com/s/1hCCkYvFjWqxvPyYA2-YElA?pwdotap 直接管理员身份双击安装&#xff0c;安装成功后会在任务栏里出现这个小图标&#xff1a; 打开cmd&#xff0c;输入…

第1章 基础知识

第1章 基础知识 1.1 机器语言 机器语言就是机器指令的集合&#xff0c;机器指令展开来讲就是一台机器可以正确执行的命令 1.2 汇编语言的产生 汇编语言的主题是汇编指令。汇编指令和机器指令的差别在于指令的表示方法上&#xff0c;汇编指令是机器指令便于记忆的书写格式。…

免费分享一套SpringBoot+Vue在线水果(销售)商城管理系统【论文+源码+SQL脚本】,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue在线水果(销售)商城管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue在线水果(销售)商城管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue在线水果(销售)商…

linux基于wifi,Xshell的远程连接

最近有个比赛&#xff0c;要使用ros小车但是系统是ubuntu20.04无桌面系统刚开始接触linux的我啥都不会&#xff0c;就一个简单的连接wifi都搞了3天才搞通。再此进行一个总结。参考博客原文链接&#xff1a;https://blog.csdn.net/qq_51491920/article/details/126221940 一、什…

非最大值抑制(NMS)函数

非最大值抑制&#xff08;NMS&#xff09;函数 flyfish 非最大值抑制&#xff08;Non-Maximum Suppression, NMS&#xff09;是计算机视觉中常用的一种后处理技术&#xff0c;主要用于目标检测任务。其作用是从一组可能存在大量重叠的候选边界框中&#xff0c;筛选出最具代表…

从CVPR 2024看域适应、域泛化最新研究进展

域适应和域泛化一直以来都是各大顶会的热门研究方向。 域适应指&#xff1a;当我们在源域上训练的模型需要在目标域应用时&#xff0c;如果两域数据分布差异太大&#xff0c;模型性能就有可能降低。这时可以利用目标域的无标签数据&#xff0c;通过设计特定方法减小域间差异&a…

thinksboard 新建子类菜单

新建需要的文件 打开bz-routing.module.ts文件&#xff0c;设置bzRoutes&#xff0c;为下面使用 import { Injectable, NgModule } from angular/core; import { Resolve, RouterModule, Routes } from angular/router; import { Authority } from shared/models/authority.en…

【创建者模式-工厂模式】

简单工厂模式 &#xff08;也称为静态工厂模式&#xff09;由一个工厂对象负责创建所有产品类的实例。客户端通过传入一个参数给工厂类来请求创建哪种产品类的实例。这种模式的优点在于客户端不需要知道具体的产品类&#xff0c;只需要知道对应的参数即可。缺点是当需要添加新…

redis复习

redis知识点 redis持久化redis 订阅发布模式redis主从复制哨兵模式redis雪崩&#xff0c;穿透缓存击穿&#xff08;请求太多&#xff0c;缓存过期&#xff09;缓存雪崩 redis持久化 redis是内存数据库&#xff0c;持久化有两种方式&#xff0c;一种是RDB&#xff08;redis dat…

【解决方案】你必须要知道的~前端九种跨域方式实现原理(完整版)

前言 前后端数据交互经常会碰到请求跨域&#xff0c;什么是跨域&#xff0c;以及有哪几种跨域方式&#xff0c;这些问题通常出现在Web开发中&#xff0c;当浏览器执行脚本发起请求到不同的域名、协议或端口时&#xff0c;出于安全考虑&#xff0c;浏览器会限制这种跨源HTTP请求…

Redis数据库(六):主从复制和缓存穿透及雪崩

目录 一、Redis主从复制 1.1 概念 1.2 主从复制的作用 1.3 实现一主二从 1.4 哨兵模式 1.4.1 哨兵的作用 1.4.2 哨兵模式的优缺点 二、Redis缓存穿透和雪崩 2.1 缓存穿透——查不到 2.1.1 缓存穿透解决办法 2.2 缓存击穿 - 量太大&#xff0c;缓存过期 2.2.1 缓存…

拍照就用华为Pura 70系列,后置真实感人像轻松出片!

平时喜欢用手机记录生活的人是不是总有个烦恼&#xff0c;想要拍出媲美单反的完美人像&#xff0c;又怕照片失真&#xff0c;经过近期对手机摄影的探索&#xff0c;我发现了华为Pura70系列的真实感人像之美&#xff0c;它给予每个热爱生活的人直面镜头的自信&#xff0c;记录真…

毕业季留念,就该这样记录下来

毕业季来啦&#xff01;这个季节总是充满了不舍和期待&#xff0c;就像夏天里的冰淇淋&#xff0c;甜蜜中带着一丝丝凉意。在这个特别的时刻&#xff0c;我想和大家分享一款陪伴我记录青春点滴的神器——nova 12 Ultra 手机。 要说自拍&#xff0c;我可是个“资深玩家”。以前…