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.
async与await - 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创建的线程都是后台线程)
- 前台线程: 前台线程是不会被立即关闭的,它的关闭只会发生在自己执行完成时,不受外在因素的影响。假如应用程序退出,造成它的前台线程终止,此时CLR仍然保持活动并运行,使应用程序能继续运行,当它的的前台线程都终止后,整个进程才会被销毁。
- 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.async 与 await
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}");
}

相关文章:
C# 异步编程详解(Task,async/await)
文章目录 1.什么是异步2.Task 产生背景3.Thread(线程) 和 Task(异步)的区别3.1 几个名词3.2 Thread 与 Task 的区别 4.Task API4.1 创建和启动任务4.2 Task 等待、延续和组合4.3 task.Result4.4 Task.Delay() 和 Thread.Sleep() 区别 5.CancellationToken 和 CancellationToken…...
qt结合vs2022安装
进入清华大学开源软件: 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 下载完成后,双击进行安装: 进入邮箱进行验证: 可能是因为网络问题,无法安装。 重新安装5.12.12版本。 安装后启动失败,重新…...
Kafka集群部署(手把手部署图文详细版)
1.1.1 部署zookpeer 在node02下载并解压zookeeper软件包 cd /usr/local wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz 或者:scp cat192.168.28.100:/home/cat/zookeeper-3.4.6.tar.gz /tmp(注意目录…...
阿里Qwen2-72B大模型已是开源榜的王者,为什么还要推出其他参数模型,被其他模型打榜?
6 月 27 日,全球知名的开源平台 Hugging Face 的联合创始人兼首席执行官 Clem 在社交平台激动宣布,阿里 Qwen2-72B 成为了开源模型排行榜的王者。 这是一件大好事,说明了我们在大模型领域从先前的追赶,逐渐走向了领导,…...
7.基于SpringBoot的SSMP整合案例-表现层开发
目录 1.基于Restfu1进行表现层接口开发 1.1创建功能类 1.2基于Restful制作表现层接口 2.接收参数 2使用Apifox测试表现层接口功能 保存接口: 分页接口: 3.表现层一致性处理 3.1先创建一个工具类,用作后端返回格式统一类:…...
【server】3、注册中心与配置中心
1、服务注册与发现 1.1、consul 1.1.1 是什么 官网: Consul by HashiCorp spring-cloud-consul: Spring Cloud Consul :: Spring Cloud Consul gitHub 官网 :GitHub - hashicorp/consul: Consul is a distributed, highly available, and data cent…...
【大数据】—量化交易实战案例(海龟交易策略)
声明:股市有风险,投资需谨慎!本人没有系统学过金融知识,对股票有敬畏之心没有踏入其大门,今天用另外一种方法模拟炒股,后面的模拟的实战全部用同样的数据,最后比较哪种方法赚的钱多。 海龟交易…...
014-GeoGebra基础篇-快速解决滑动条的角度无法输入问题
有客户反馈,他的Geogebra一直有个bug,那就是输入角度最大值时总不按照他设定的展示,快被气炸了~ 目录 一、问题复现(1)插入一个滑动条(2)选择Angle(3)输入90,…...
Diffusion模型的微调和引导
留意后续更新,欢迎关注微信公众号:组学之心 Diffusion模型的微调和引导 微调(fine-tuning): 从一个已经训练过的模型开始训练,我们就可以从一个学会如何“去噪”的模型开始训练,相对于随机初始…...
零基础学MySQL:从入门到实践的完整指南
引言: MySQL,作为全球最受欢迎的开源关系型数据库管理系统之一,以其高性能、易用性和灵活性,在Web开发、数据分析等领域占据着举足轻重的地位。如果你是一位编程新手,想要踏入数据库管理的大门,本文将从零…...
澳蓝荣耀时刻,6款产品入选2024年第一批《福州市名优产品目录》
近日,福州市工业和信息化局公布2024年第一批《福州市名优产品目录》,澳蓝自主研发生产的直接蒸发冷却空调、直接蒸发冷却组合式空调机组、间接蒸发冷水机组、高效间接蒸发冷却空调机、热泵式热回收型溶液调湿新风机组、防火湿帘6款产品成功入选。 以上新…...
Frrouting快速入门——OSPF组网(一)
FRR简介 FRR是FRRouting的简称,是一个开源的路由交换软件套件。其作者源自老牌项目quaga的成员,也可以算是quaga的新版本。 使用时一般查看此文档:https://docs.frrouting.org/projects/dev-guide/en/latest/index.html FRR支持的协议众多…...
记录通过Cloudflare部署属于自己的docker镜像源
引言 由于最近国内无法正常拉取docker镜像,然而找了几个能用的docker镜像源发现拉取回来的docker镜像不是最新的版本,部署到Cloudflare里Workers 和 Pages,拉取docker 镜像成功,故记录部署过程。 部署服务 登录Cloudflare后&…...
波动方程 - 在三维图中动态显示二维波动方程的解就像水面波澜起伏
波动方程 - 在三维图中动态显示二维波动方程的解就像水面波澜起伏 flyfish 波动方程的求解结果通常不是一个单一的数值,而是一个函数或一组函数,这些函数描述了波随时间和空间的传播情况。具体来说,波动方程的解可以是关于时间和空间变量的…...
yum命令提示 错误:rpmdb: BDB0113 Thread/process 4153/139708200269632
一、报错信息 [rootDawn yum.repos.d]# yum clean all 错误:rpmdb: BDB0113 Thread/process 4153/139708200269632 failed: BDB1507 Thread died in Berkeley DB library 错误:db5 错误(-30973) 来自 dbenv->failchk:BDB0087 DB_RUNRECOVE…...
欢乐钓鱼大师游戏攻略:在什么地方掉称号鱼?云手机游戏辅助!
《欢乐钓鱼大师》是一款融合了休闲娱乐和策略挑战的钓鱼游戏。游戏中的各种鱼类不仅各具特色,而且钓鱼过程充满了挑战和乐趣。下面将为大家详细介绍如何在游戏中钓鱼,以及一些有效的钓鱼技巧,帮助你成为一个出色的钓鱼大师。 实用工具推荐 为…...
什么是构造函数?Java 中构造函数的重载如何实现?
构造函数,就像是建筑房屋时的奠基仪式,是Java类中一个特殊的方法,主要用于初始化新创建的对象。 每当创建一个类的新实例时,构造函数就会自动调用,负责为这个新对象分配内存,并对其进行必要的设置…...
Linux内核 -- ARMv7 与 ARMv8 中的 asmlinkage 作用及使用
ARMv7 与 ARMv8 中的 asmlinkage 作用及使用 asmlinkage 是一个宏,通常在内核代码中使用,用于定义调用约定,特别是指定函数的参数是通过栈传递而不是通过寄存器。它主要用于内核与汇编之间的接口函数,使得参数传递更加一致和明确…...
GPT提示词模板
BRTR 原则 # 背景(Background) - 描述任务的背景信息,包括任务的起因、目的、相关的历史信息或当前状况。 - 提供足够的背景信息以便让ChatGPT理解任务的上下文。 # 角色(Role) - 定义ChatGPT在任务中所扮演的角色&…...
WRF学习——使用CMIP6数据驱动WRF/基于ncl与vdo的CMIP6数据处理
动力降尺度 国际耦合模式比较计划(CMIP)为研究不同情景下的气候变化提供了大量的模拟数据,而在实际研究中,全球气候模式输出的数据空间分辨率往往较低(>100Km,缺乏区域气候特征,为了更好地研…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...
