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}");
}

在这里插入图片描述

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

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

相关文章

最年轻获奖者诞生!一文带你了解历届国家最高科学技术奖获奖人

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨沛贤 深度好文&#xff1a;4000字丨15分钟阅读 作为国家层面面向科学、技术领域的最高级别奖励&#xff0c;国家最高科学技术奖于 2000 年由国务院设立&#xff0c;每年评选…

解锁分布式云多集群统一监控的云上最佳实践

作者&#xff1a;在峰 引言 在当今数字化转型加速的时代&#xff0c;随着混合云、多云多集群环境等技术被众多企业广泛应用&#xff0c;分布式云架构已成为众多企业和组织推动业务创新、实现弹性扩展的首选&#xff0c;分布式云容器平台 ACK One&#xff08;Distributed Clou…

OpenGL3.3_C++_Windows(21)

抗锯齿 遇到模型边缘有锯齿&#xff1a;光栅器将顶点数据转化为片段的方式有关 抗锯齿&#xff1a;产生更平滑的边缘SSAA超采样抗锯齿&#xff1a;使用比正常分辨率更高的分辨率&#xff0c;来渲染场景&#xff0c;它也会带来很大的性能开销。 光栅器&#xff1a; 位于最终处…

vi编辑器的常用方法

一、背景描述 在我们连接操作Linux服务器的时候&#xff0c;需要对其配置文件等内容进行一些增删改的操作&#xff0c;一般情况下我们直接使用Linux系统自带vi编辑器进行相应的操作&#xff0c;熟悉vi的常用功能对于我们编辑一些较大的文件效率能够有所提升&#xff0c;使用起来…

全国公共汽车、出租车拥有情况及客运量、货运量数据

基本信息. 数据名称: 全国公共汽车、出租车拥有情况及客运量、货运量数据 数据格式: Shp、Excel 数据时间: 2020-2022年 数据几何类型: 面 数据坐标系: WGS84 数据来源&#xff1a;中国城市统计年鉴 数据可视化. 2022年全年公共汽车客运总量数据示意图 2022年公路客…

Python 基础:使用 unittest 模块进行代码测试

目录 一、测试函数2.1 通过案例2.2 不通过案例2.3 添加新测试 二、测试类2.1 单个测试案例2.2 多个测试案例 三、总结 遇到看不明白的地方&#xff0c;欢迎在评论中留言呐&#xff0c;一起讨论&#xff0c;一起进步&#xff01; 本文参考&#xff1a;《Python编程&#xff1a;…

第六十八:iview里的table,每行数据如果有满足条件的怎么更改颜色

当然了&#xff0c;肯定又是插槽&#xff0c;话不多说直接贴图 话不多说&#xff0c;直接贴代码 <Table :columns"columns" :data"data" border show-summary height"300" sum-text"汇总" v-else :summary-method"handleSumma…

YOLO目标检测综述(2024.6月最新!)

1 基本概念 目标检测&#xff08;Object Detection&#xff09;是计算机视觉领域的重要任务之一&#xff0c;旨在识别图像或视频中的特定目标并将其位置标记出来。与图像分类任务不同&#xff0c;目标检测要求不仅能够识别目标类别&#xff0c;还需要精确地定位目标的位置。由于…

OpenAI封锁中国?国产大模型开启价格战?收好这份LLM选购指南,带你搞定极致性价比 | ShowMeAI

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; 1. Cloud LLM capability, cost, performance | 一份开发者最实用的大模型「性价比」计算手册 这是 Harlan Lewis 整理的大语言模型 (LLM) 对比清单…

3d合并模型一直加载有哪些原因---模大狮模型网

当在3D软件中合并3d模型时&#xff0c;可能会遇到加载时间过长或持续加载的情况。这可能是由以下原因之一引起的&#xff1a; 一&#xff1a;模型复杂度 合并的模型可能非常复杂&#xff0c;包含大量的面片、顶点或纹理等。这会增加加载和处理的时间。解决方法是优化模型&…

【第二周】基础语法学习

目录 前言初始化项目文件介绍基本介绍JSWXMLWXSS 常见组件基础组件视图容器match-mediamovable-area/viewpage-containerscroll-viewswiper 表单组件自定义组件 模板语法数据绑定单向数据绑定双向数据绑定 列表渲染条件渲染模板引用 事件系统事件类型事件绑定阻止冒泡互斥事件事…

股指期权交割日期是什么时候?在每个月几号?

今天带你了解股指期权交割日期是什么时候&#xff1f;在每个月几号&#xff1f;期权交割日是指合约到期之日&#xff0c;即投资者需要履行买卖合约的义务。 股指期权的交割日期通常是期权合约到期日的第三个星期五。如果这一天是公共假日&#xff0c;则交割日可能会提前到前一…

Transformers 安装及 google-t5/t5-small 机器翻译示例

文章目录 Github文档推荐文章简介安装官方示例google-t5/t5-small使用脚本进行训练Pytorch 机器翻译数据集下载数据集格式转换 Github https://github.com/huggingface/transformers 文档 https://huggingface.co/docs/transformers/indexhttps://github.com/huggingface/tr…

计算机二级Access操作题总结——综合应用

属性表相关 例1&#xff1a; 不允许输入和修改其中的数据→【是否锁定】 例2&#xff1a; 单击“退出”按钮(名为“bt2”)&#xff0c;调用设计好的宏“mEmp”来关闭窗体。 分组和汇总 对“rSell”报表进行适当设置&#xff0c;使每名雇员的姓名显示在该雇员所售书籍信…

三品PDM项目成功上线 垣发集团携手三品软件迈向智能未来

项目背景 随着全球工业化和城市化进程的不断加快&#xff0c;高空作业车的市场需求日益增长。河南垣发专用车辆集团有限公司&#xff08;以下简称“垣发集团”&#xff09;自2014年成立以来&#xff0c;一直专注于高空作业车系列产品的研发与制造。 作为一家科技导向型企业&am…

Java AWT BorderLayout的使用

目录 背景: 代码例子: 代码详解: 效果展示: 总结: 背景: BoderLayout是Java AWT(Abstract Window Toolkit)和Swing图形用户界面(GUI)库中的一个布局管理器。它用于安排组件(如按钮、标签、面板等)在容器(如窗户、面板等)中的位置。BorderLayout容器划分为五个区域:北(NO…

硬盘空间告急?监控服务器容量,钉钉及时提醒!

在日常的服务器维护中&#xff0c;硬盘容量的监控是非常重要的。如果硬盘容量超过某个阈值&#xff0c;可能会导致服务器无法正常运行&#xff0c;影响业务的正常运作。为了避免这种情况&#xff0c;我们可以编写一个Shell脚本&#xff0c;定期检查硬盘容量&#xff0c;当超过设…

微服务框架中的Eureka和Ribbon的个人理解

微服务框架需要学习的东西很多&#xff0c;基本上我把它分为了五个模块&#xff1a; 第一&#xff1a;微服务技术模块 分为三个常用小模块&#xff1a; 1.微服务治理&#xff1a; 注册发现 远程调用 配置管理 网关路由 2.微服务保护&#xff1a; 流量控制 系统保护 熔断降级 服…

springcloud第4季 分布式事务seata实现AT模式案例

一 seata案例 1.1 背景说明 本案例使用seata的at模式&#xff0c;模拟分布式事务场景&#xff1a;【下订单&#xff0c;减库存&#xff0c;扣余额&#xff0c;改状态】 1.2 初始化脚本 1.2.1 新建seata_order_024 库 1.新建undol_log表 -- for AT mode you must to init…

锐捷AP从其它项目拆下,怎么也加入不了到现在这个网络里来

环境: AP 产品型号:RG-RAP2260G 问题描述: 锐捷AP从其它项目拆下,怎么也加入不了到现在这个网络里来,现网是WIFI5的,想把2260G用来升级,恢复出厂设置后,插上网线,现网找不到这个AP 解决方案: 1.通电重置AP后,连接AP WiFi进入管理页面,要求先快速配置 2.开始配置…