(十)异步-什么是异步方法(2)

news2024/9/24 9:25:00

一、什么是异步方法

异步方法在完成其工作之前即返回到调用方法,然后在调用方法继续执行的时候完成其工作。

特点:

  • 方法头中包含 async 方法修饰符。
  • 包含一个或多个 await 表达式,表示可以异步完成的任务。
  • 必须具备3种返回类型之一:
    • void
    • Task
    • Task<T>
    • ValueTask<T>
  • 任何具有公开可访问的 GetAwaiter 方法的类型。
  • 异步方法的形参可以为任意类型、任意数量,但不能为 out 或 ref 参数。
  • 按照约定,异步方法的名称应该以 Async 为后缀。
  • 除了方法以外, Lambda 表达式和匿名方法也可以作为异步对象。
async Task<int> CountCharactersAsync(int id,string site)
{
Console.WriteLine("Starting CountCharacters");
WebClient wc = new WebClient();
//await 表达式
string result = await wc.DownloadStringTaskAsync( new Uri(site) );
//Lambda 表达式
// var result = await Task.Run(() => wc.DownloadString(new Uri(site)));
return result = Length;
}

async 关键字

  • 异步方法的方法头必须包含 async 关键字,且必须位于返回类型之前。
  • 该修饰符只是标识该方法包含一个或多个 await 表达式。它本身并不能创建任何异步操作。
  • async 关键字是一个上下文关键字。除了作为方法修饰符(或 Lambda 表达式修饰符、匿名方法修饰符)之外,async 还可用作标识符。(比如 int async = 0;)

返回类型:

  • 1)Task: 如果调用方法不需要从异步方法中返回某个值,但需要检查异步方法的状态,那么异步方法可以返回一个 Task 类型的对象。如果异步方法中包含任何 return 语句,则它们不能返回任何东西:
Task someTask = DoStuff.CalculateSumAsync(5,6);
...
someTask.Wait();
  • 2)ask<T>: 如果调用方法要从调用中获取一个 T 类型的值,异步方法的返回类型就必须是Task<T>。调用方法将通过读取 Task 的 Result 属性来获取整个 T 类型的值。
Task<T> value = DoStuff.CalcuateSumAsync(5,6);
...
Console.WriteLine($"Value:{ value.Result }");
  • 3)ValueTask<T>: 这是一个值类型对象,它与 Task<Tgt; 类似,但用于任务结果可能已经可用的情况。因为它是一个值类型,所以它可以放在栈上。,而无须像 Task<T> 对象那样在堆上分配空间。
    class Program
    {
        static void Main(string[] args)
        {
            ValueTask<int> value = DoAsynStuff.CalculateSumAsync(0, 6);
            //处理其他事情
            Console.WriteLine($"Value:{ value.Result }");
            value = DoAsynStuff.CalculateSumAsync(5, 6);
            //处理其他事情
            Console.WriteLine($"Value:{ value.Result }");
            Console.ReadKey();
        }

        static class DoAsynStuff
        {
            public static async ValueTask<int> CalculateSumAsync(int i1,int i2)
            {
                // 如i1 == 0,则可以避免执行长时间运行的任务
                if(i1 == 0)
                {
                    return i2; 
                }

                int sum = await Task<int>.Run(() => GetSum(i1, i2));
                return sum;
            }

            private static int GetSum(int i1,int i2){  return i1 + i2; }
        }

    }

我的编程环境不支持ValueTask<T>,查了相关资料可知,可能.NET 5.0 版本及以上才支持,而我电脑里最高版本才 4.6。算了,我就不测试了,先把例子贴上去,以后有条件了(不想再花时间安装 .NET 框架)再研究。

  • 4)void: 如果调用方法仅仅想执行异步方法,而不需要与它做任何进一步的交互时,异步方法可以返回 void 类型。如果异步方法中包含任何 return 语句,则它们不能返回任何东西。(这跟返回类型 Task 一样,包含 await时,等待任务完成之前,还可以执行下一个代码块,否则,等待任务完成才能执行下一步)

void 和 Task 的区别代码例子:


   class MyDownloadString
    {
        const string Str = "https://www.baidu.com/";
        Stopwatch sw = new Stopwatch();

        public async void DoRun()
        {
            sw.Start();

            await returnTaskValue();
            Console.WriteLine("----------:{0,4:N0}", sw.Elapsed.TotalMilliseconds);
            returnVoidValue();
            Console.WriteLine("++++++++++:{0,4:N0}", sw.Elapsed.TotalMilliseconds);

            Thread.Sleep(500);
            Console.WriteLine();
            Console.WriteLine("暂停线程,为的是测试Task<string>,跟 Task 和 void 的无关:");

            Task<string> t = CountCharacters("Task<string>");
            Console.WriteLine("**********:{0,4:N0}", sw.Elapsed.TotalMilliseconds);
            //总结:
            //1、如果 Task 和 void 异步方法里有 await,则方法内需要等待完成才能返回
            //假设 Task 和 void 异步方法内有 await:
            //2、在同层级方法内调用异步方法,Task 方法 添加 await 等待任务完成才能执行下一个语句
            //void 方法并不会等待任务完成,而继续执行下一个语句

            //3、 Task<string> 在同一层方法被调用,则执行异步处理。跟 void 一样。

            //4、同一层方法作为调用者,如果被调用者为异步方法时:
            //执行 void 和 Task<string>方法,不需要 await,则执行异步处理。
            //执行 Task 方法 需要 await 执行等待任务完成,才能执行下一个语句;否则,就异步处理。

        }

        private async Task returnTaskValue()
        {
            Console.WriteLine("Task_Start:{0,4:N0}", sw.Elapsed.TotalMilliseconds);
            var result = await Task.Run(() => CountCharacters("Task"));
            Console.WriteLine("Task_End:{0,4:N0}", sw.Elapsed.TotalMilliseconds);
        }

        private async void returnVoidValue()
        {
            Console.WriteLine("Void_Start:{0,4:N0}", sw.Elapsed.TotalMilliseconds);
            var result = await Task.Run(() => CountCharacters("Void"));
            Console.WriteLine("Void_End:{0,4:N0}", sw.Elapsed.TotalMilliseconds);
        }

        private async Task<string> CountCharacters(string strId)
        {
            WebClient wc1 = new WebClient();
            Console.WriteLine("{0}_DownloadBefore:{1,4:N0}", strId, sw.Elapsed.TotalMilliseconds);
            var result = await wc1.DownloadStringTaskAsync(new Uri("https://www.baidu.com/"));
            Console.WriteLine("{0}_DownloadAfter:{1,4:N0}", strId, sw.Elapsed.TotalMilliseconds);
            return result;
        }
    }

输出结果:

>Task_Start:   1
Task_DownloadBefore:  68
Task_DownloadAfter: 420
Task_End: 435
----------: 435
Void_Start: 436
Void_DownloadBefore: 437
++++++++++: 440
Void_DownloadAfter: 469
Void_End: 469

暂停线程,为的是测试Task<string>,跟 Task 和 void 的无关:
Task<string>_DownloadBefore: 950
**********: 952
Task<string>_DownloadAfter: 983

void 返回类型使用“调用并忘记”的异步方法:

 class Program
    {
        static void Main(string[] args)
        {
            DoAsynStuff.CalculateSumAsync(5, 6);
            //如果参数值为1,则先等待void 方法执行完成之后才能执行下一个语句
            //这样做法会有安全隐患,适合 void 异步方法处理时间短,Sleep 参数值设置足够大
            Thread.Sleep(200);
            Console.WriteLine("Program Exiting");
            Console.ReadKey();
        }

        static class DoAsynStuff
        {
            public static async void CalculateSumAsync(int i1,int i2)
            {
                int value = await Task.Run(() => GetSum(i1, i2));
                Console.WriteLine("Value:{0}", value);
            }

            private static int GetSum(int i1,int i2)
            {
                return i1 + i2;
            }
        }

    }
  • 任何具有可访问的 GetAwaiter 方法的类型。

二、异步方法的控制流

异步方法的结构包含三个不同区域:

  • await 表达式之前的部分
  • await 表达式:异步执行的任务
  • await 表达式之后的部分

图-阐明了一个异步方法的控制流

请添加图片描述

目前有两个控制流: 一个在异步方法内,一个在调用方法内。
当后续部分遇到 retun 语句或到达方法末尾时:

  • 如果方法的返回类型为 void,控制流将退出。
  • 如果方法的返回类型为 Task,则后续部分设置 Task 的状态属性并退出。如果返回类型为
    Task<T>ValueTask<T>,则后续部分还将设置对象的 Result 属性。

1、await 表达式

await 表达式指定了一个异步执行的任务。

await task //task:空闲对象

一个空闲对象即是一个 awaitable 类型的实例。 awaitable 类型是指包含 GetAwaiter 方法的类型,该方法没有参数,返回一个 awaiter 类型的对象。

awaiter 类型包含以下成员:

bool IsCompleted{ get; }
void OnCompleted(Action);

void GetResult();
T GetResult();

2、关于 Task.Run:

使用 Task.Run 方法来创建一个 Task。

//是以 Func<TReturn> 委托为参数
public static Task Run(Func<TReturn> function);
//还有其他重载的类型
...

创建委托三种实现方式:

    class MyClass
    {
        public int Get10()
        {
            return 10;
        }

        public async Task DoWorkAsync()
        {
            Func<int> ten = new Func<int>(Get10);
            int a = await Task.Run(ten);
            int b = await Task.Run(new Func<int>(Get10));
            int c = await Task.Run(()=> { return 10; });

            Console.WriteLine($"{ a } { b } { c }");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Task t = (new MyClass()).DoWorkAsync();
            t.Wait();
            Console.ReadKey();
        }

    }

输出结果:

10 10 10

表-Task.Run 重载的返回类型和签名

返回类型签名
TaskRun( Action action)
TaskRun( Action action, CancellationToken token )
Task<TResult>Run( Func<TResult> function )
Task<TResult>Run( Func<TResult> function, CancellationToken token)
TaskRun( Func<Task> function )
TaskRun( Func<Task> function, CancellationToken token )
Task<TResult>Run( Func<Task<TResult>> function )
Task<TResult>Run( Func<Task<TResult>> function, CancellationToken token )

表-可作为 Task.Run 方法第一个参数的委托类型

委托类型签名含义
Actionvoid Action()不需要参数且无返回值的方法
Func<TResult>TResult Func()不需要参数,但返回 TRsult 类型对象的方法
Func<Task>Task Func()不需要参数,但返回简单 Task 对象的方法
Func<Task<TResult>>Task<TResult> Func()不需要参数,但返回 Task<T> 类型对象的方法

4个 await 语句的示例:

static class MyClass
    {
        public static async Task DoWorkAsync()
        {
            //Run(Action)
            await Task.Run(() => Console.WriteLine(5.ToString()));
            //Run(TResult Func())
            Console.WriteLine((await Task.Run(() => 6)).ToString());
            //Run(Task Func())
            await Task.Run(() => Task.Run(() => Console.WriteLine(7.ToString())));
            //Run(Task<TResult> Func())
            int value = await Task.Run(() => Task.Run(() => 8));

            Console.WriteLine(value.ToString());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Task t = MyClass.DoWorkAsync();
            t.Wait();
            Console.WriteLine("Press Enter key to exit");
            Console.ReadKey();
        }
    }

Lambda 函数() = > GetSum(5,6) 满足 Func<TResult> 委托

int value = awaite Task.Run(() => GetSum(5,6));

三、取消一个异步操作

一些 .NET 异步方法允许你请求终止执行。
System.Threading.Tasks 命名空间中有两个类是为此目的而设计的:CancellationToken 和 CancellationTokenSource。

  • CancellationToken 对象包含一个任务是否应被取消的信息。
  • 拥有 CancellationToken 对象的任务需要定期检查其令牌(token)状态。如果 CancellationToken 对象的 IsCancellationRequested 属性为 true,任务需停止其操作并返回。
  • CancellationToken 是不可逆的,并且只能使用一次。也就是说,一旦 IsCancellationRequested 属性被设置为 true,就不能更改了。
  • CancellationTokenSource 对象创建可分配给不同任务的 CancellationToken 对象。任务持有 CancellationTokenSource 的对象都可以调用其 Cancel 方法,这会将 CancellationTToken 的 IsCancellationRequested 属性设置为 true。

使用这两个类,触发取消行为:

 class Program
    {
        static void Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken token = cts.Token;

            MyClass mc = new MyClass();
            Task t = mc.RunAsync(token);

            //Thread.Sleep(3000);
            //cts.Cancel();

            t.Wait();
            Console.WriteLine($"Was Cancelled:{ token.IsCancellationRequested }");

            Console.ReadKey();
        }

        class MyClass
        {
            public async Task RunAsync(CancellationToken ct)
            {
                if (ct.IsCancellationRequested)
                    return;

                await Task.Run(() => CycleMethod(ct), ct);
            }

            void CycleMethod(CancellationToken ct)
            {
                Console.WriteLine("Starting CycleMethod");
                const int max = 5;
                for(int i = 0; i < max;i++)
                {
                    if (ct.IsCancellationRequested)
                        return;
                    Thread.Sleep(1000);
                    Console.WriteLine($"{ i + 1 } of { max } iterations completed");
                }
            }
        }
    }

输出结果:

Starting CycleMethod
1 of 5 iterations completed
2 of 5 iterations completed
3 of 5 iterations completed
4 of 5 iterations completed
5 of 5 iterations completed
Was Cancelled:False

若在 Main 方法里恢复执行这两行代码:

Thread.Sleep(3000);//3秒后取消任务执行
cts.Cancel();

输出结果:

Starting CycleMethod
1 of 5 iterations completed
2 of 5 iterations completed
3 of 5 iterations completed
Was Cancelled:True

四、在调用方法中同步地等待任务

你的代码会继续执行其他任务,但在某个点上可能会需要等待某个特殊 Task 对象完成,然后再继续。

  static class MyDownloadString
    {
        public static void DoRun()
        {
            Task<int> t = CountCharactersAsync("http://illustratedcsharp.com");
            t.Wait();//等待任务 t 结束
            Console.WriteLine($"The task has finished,returning value { t.Result }.");
        }

        private static async Task<int> CountCharactersAsync(string site)
        {
            string result = await new WebClient().DownloadStringTaskAsync(new Uri(site));
            return result.Length;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyDownloadString.DoRun();
            Console.ReadKey();
        }
    }

输出结果:

The task has finished,returning value 5164.

五、Wait 方法用于单一 Task 对象。

1、使用 WaitAll 和 WaitAny

对于一组 Task ,可以等待所有任务都结束,也可以等待某一个任务结束。

  • WaitAll
  • WaitAny
      class MyDownloadString
    {
        Stopwatch sw = new Stopwatch();

        public void DoRun()
        {
            sw.Start();

            Task<int> t1 = CountCharactersAsync(1,"http://illustratedcsharp.com");
            Task<int> t2 = CountCharactersAsync(2, "http://illustratedcsharp.com");

            //Task.WaitAll(t1, t2);
            //Task.WaitAny(t1, t2);

            Console.WriteLine("Task 1: {0} Finished",t1.IsCompleted?"":"Not");
            Console.WriteLine("Task 2: {0} Finished", t2.IsCompleted ? "" : "Not");
            Console.Read();
        }

        private  async Task<int> CountCharactersAsync(int id,string site)
        {
            WebClient wc = new WebClient();
            string result = await wc.DownloadStringTaskAsync(new Uri(site));
            Console.WriteLine("  Call {0} completed:    {1,4:N0} ms", id, sw.Elapsed.TotalMilliseconds);
            return result.Length;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyDownloadString ds = new MyDownloadString();
            ds.DoRun();
            Console.ReadKey();
        }
    }

输出结果:

不使用 WaitAll 和 WaitAny 方法时,t1 和 t2 异步执行

Task 1: Not Finished
Task 2: Not Finished
Call 1 completed: 598 ms
Call 2 completed: 600 ms

1)如果在 DoRun 方法里,恢复 Task.WaitAll(t1, t2); 代码:

Task.WaitAll(t1, t2);
//Task.WaitAny(t1, t2);

输出结果:
使用了 WaitAll 方法后,所有任务都要等待完成,之后代码才能继续执行。

Call 2 completed: 695 ms
Call 1 completed: 617 ms
Task 1: Finished
Task 2: Finished

2)如果在 DoRun 方法里,恢复 Task.WaitAny(t1, t2); 代码:

//Task.WaitAll(t1, t2);
Task.WaitAny(t1, t2);

输出结果:

使用了 WaitAll 方法后,至少有一个 Task 需要等待完成才能继续执行。

Call 2 completed: 659 ms
Call 1 completed: 658 ms
Task 1: Not Finished
Task 2: Finished

2、WaitAll 和 WaitAny 分别还包含4个重载。

签名描述
WaitAll
void WaitAll(params Task[] tasks)等待所有任务完成
bool WaitAll(Task[] tasks, int millisecondsTimeout)等待所有任务完成。如果在超时时限内没有全部完成,则返回 false 并继续执行
void WaitAll(Task[] tasks, CancellationToken token)等待所有任务完成,或等待 CancellationToken 发出取消信号
bool WaitAll(Task[] tasks, TimeSpan span)等待所有任务完成。如果在超时时限内没有全部完成,则返回 false 并继续执行
bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken token)等待所有任务完成,或等待 CancellationToken 发出取消信号。如果在超时时限内没有发生上述情况,则返回 false 并继续执行
WaitAny
void WaitAny(params Task[] tasks)等待任意一个任务完成
bool WaitAny(Task[] tasks, int millisecondsTimeout)等待任意一个任务完成。如果在超时时限内没有完成的,则返回 false 并继续执行
void WaitAny(Task[] tasks, CancellationToken token) 等待任意一个任务完成,或等待 CancellationToken 发出取消信号
bool WaitAny(Task[] tasks, TimeSpan span)等待任意一个任务完成。如果在超时时限内没有完成的,则返回 false
bool WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken token)等待任意一个任务完成,或等待 CancellationToken 发出取消信号。如果在超时时限内没有发生上述情况,则返回 false 并继续执行

3、在异步方法中异步地等待任务

可以通过 Task.WhenAll 和 Task.WhenAny 方法,来实现在异步方法中等待一个或所有任务完成。这两个方法称为组合子。

     class MyDownloadString
    {
        Stopwatch sw = new Stopwatch();

        public void DoRun()
        {
            sw.Start();

            Task<int> t = CountCharactersAsync("http://www.baidu.com", "http://illustratedcsharp.com");

            Console.WriteLine("DoRun:   Task {0}Finshed", t.IsCompleted ?"":"Not");
            Console.WriteLine("DoRun:   Result = {0}", t.Result);
        }

        private  async Task<int> CountCharactersAsync(string site1,string site2)
        {
            WebClient wc1 = new WebClient();
            WebClient wc2 = new WebClient();
            Task<string> t1 = wc1.DownloadStringTaskAsync(new Uri(site1));
            Task<string> t2 = wc2.DownloadStringTaskAsync(new Uri(site2));

            List<Task<string>> tasks = new List<Task<string>>();
            tasks.Add(t1);
            tasks.Add(t2);

            await Task.WhenAll(tasks);//关键代码语句

            Console.WriteLine("  CCA:  T1 {0}Finished", t1.IsCompleted ? "" : "Not");
            Console.WriteLine("  CCA:  T2 {0}Finished", t2.IsCompleted ? "" : "Not");

            return t1.IsCompleted ? t1.Result.Length : t2.Result.Length;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyDownloadString ds = new MyDownloadString();
            ds.DoRun();
            Console.ReadKey();
        }
    }

输出结果:
在异步方法内,需要等待所有任务完成后,之后代码才能继续执行。

DoRun: Task NotFinshed
CCA: T1 Finished
CCA: T2 Finished
DoRun: Result = 9269

若把 Task.WhenAll 方法 改为 Task.WhenAny 方法:

输出结果:
在异步方法内,至少有一个任务需要等待完成,之后代码才能继续执行。

DoRun: Task NotFinshed
CCA: T1 Finished
CCA: T2 NotFinished
DoRun: Result = 9269

六、Task.Delay 方法

Thread.Sleep 会阻塞线程,而 Task.Delay 不会阻塞线程,线程还可以继续处理其他工作。

  class Simple
    {
        Stopwatch sw = new Stopwatch();

        public void DoRun()
        {
            Console.WriteLine("Caller:Before call");
            ShowDelayAsync();
            Console.WriteLine("Caller:After call");
            
        }

        private async void ShowDelayAsync()
        {
            sw.Start();
            Console.WriteLine($"  Before Delay:{ sw.ElapsedMilliseconds }");
            await Task.Delay(1000);
            Console.WriteLine($"   After Delay : { sw.ElapsedMilliseconds }");
        }
  
    }

    class Program
    {
        static void Main(string[] args)
        {
            Simple ds = new Simple();
            ds.DoRun();   
            Console.ReadKey();
        }
    }

输出结果:

Caller:Before call
Before Delay:0
Caller:After call
After Delay : 1052

Delay 方法包含4个重载,允许以不同方法来指定时间周期,同时还允许使用 CancellationToken 对象。

签名描述
Task Delay(int millisecondsDelay)在以毫秒表示的延迟时间到期后,返回完成的 Task 对象
Task Delay(TimeSpan delay)在以 .NET TimeSpan 对象表示的延迟时间到期后,返回完成的 Task 对象
Task Delay(int millisecondsDelay, CancellationToken token)在以毫秒表示的延迟时间到期后,返回完成的 Task 对象。可通过取消令牌来取消该操作
Task Delay(TimeSpan delay, CancellationToken token)在以 .NET TimeSpan 对象表示的延迟时间到期后,返回完成的 Task 对象。可通过取消令牌来取消该操作

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

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

相关文章

使用Gradio Interface构建交互式机器学习演示

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

chatgpt赋能python:Python列表排序方法介绍

Python 列表排序方法介绍 在 Python 中&#xff0c;列表是一种非常常见的数据类型。在处理列表时&#xff0c;有时需要对列表进行排序&#xff0c;以便更方便地对其进行查找、比较、统计等操作。Python 中提供了多种排序方法&#xff0c;本文将介绍这些方法并说明其使用方法。…

chatgpt赋能python:Python中的日期遍历方法详解

Python中的日期遍历方法详解 作为一门非常流行的编程语言&#xff0c;Python中的日期遍历方法备受程序员们喜爱。它可以遍历任意时间段内的日期并进行各种操作&#xff0c;比如日期加减、日期格式转换等等。在本文中&#xff0c;我们将详细介绍Python中的日期遍历方法&#xf…

lazarus:用FpSpreadsheet快速搭建基于电子表格文件的单机程序

目录 1 简介 2 制作简单的电子表格 3 需要几个控件&#xff0c;以及简单属性设置 3.1 TsWorksheetDataset 3.2 DataSource1控件 3.3 DBNavigate控件 3.4 DBGrid 3.5 编译运行&#xff0c;插入一行数据 1 简介 在FpSpreadsheet控件包中&#xff0c;有一个专门把电子表格…

Python3 注释与运算符 | 菜鸟教程(四)

目录 一、Python3 注释 &#xff08;一&#xff09;Python 中单行注释以 # 开头 &#xff08;二&#xff09;多行注释用三个单引号 或者三个双引号 """ 将注释括起来 1、单引号&#xff08;&#xff09; 2、双引号&#xff08;"""&#xff…

14.基于XML管理Bean

基于XML管理Bean 配置Springframework 引入依赖 <!-- 基于Maven依赖传递性&#xff0c;导入spring-context依赖即可导入当前所需所有jar包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId&…

(十)异步-使用异步(3)

一、GUI 程序中的异步操作 1、在 GUI 程序中使用异步操作 在 GUI程序中&#xff0c; 首先理解关于 UI 显示变化的概念。 消息&#xff1a; UI 上的行为&#xff0c;如点击按钮、展示标签、移动窗体等。消息队列&#xff1a; 把要触发的所有消息&#xff0c;都按照相关的顺序…

【备战秋招】每日一题:2023.05-B卷-华为OD机试 - 阿里巴巴找黄金宝箱(II)

为了更好的阅读体检&#xff0c;可以查看我的算法学习博客阿里巴巴找宝箱(ll) 题目描述 贫如洗的樵夫阿里巴巴在去砍柴的路上&#xff0c;无意中发现了强盗集团的藏宝地&#xff0c;藏宝地有编号从0-N的箱子&#xff0c;每个箱子上面贴有箱子中藏有金币的数量。 从金币数量中…

【MySQL】索引的数据结构

为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构。进行数据查找时&#xff0c;首先查看查询条件是否命中某条索引&#xff0c;符合则可以通过索引查找相关数据&#xff0c;如果不符合则要全表扫描&#xff0c;即需要一条一条地查找记录&#xff0c;直到找到…

「网络编程」第二讲:网络编程socket套接字(二)_ 简单UDP网络通信程序的实现

「前言」文章是关于网络编程的socket套接字方面的&#xff0c;上一篇是网络编程socket套接字&#xff08;一&#xff09;&#xff0c;下面开始讲解&#xff01; 「归属专栏」网络编程 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」 「每篇一句」 我认…

chatgpt赋能python:Python遍历文章的SEO指南

Python遍历文章的SEO指南 Python是一种高度灵活的编程语言&#xff0c;因其易于学习和使用而为许多程序员、数据科学家和SEO专业人士所青睐。在这篇文章中&#xff0c;我们将探讨Python如何遍历文章和对SEO优化的最佳实践。 什么是Python遍历文章&#xff1f; 遍历文章是指采…

【论文阅读】Segment Anything(SAM)——可分割一切的CV大模型

【前言】随着ChatGPT席卷自然语言处理&#xff0c;Facebook凭借着Segment Anything在CV圈也算扳回一城。迄今为止&#xff0c;github的star已经超过3万&#xff0c;火的可谓一塌糊涂。作为AI菜鸟&#xff0c;可不得自己爬到巨人肩膀上瞅一瞅~ 论文地址&#xff1a;https://arxi…

NDK编译C++源码生成Android平台so文件(opencv_android)

1.准备CPP文件 编写CMakeLists.txt 编写 mk文件 android-8 is unsupported. Using minimum supported version android-16 APP_PLATFORM android-16 is higher than android:minSdkVersion 1 in ./AndroidManifest.xml 修改Application.mk中的APP_PLATFORM为 android-21 builg…

59、基于51单片机多机 NRF24L01 无线温湿度 DHT11报警系统设计(程序+原理图+PCB源文件+参考论文+开题报告+任务书+元器件清单等)

摘 要 温湿度在工农业生产中占有很重要的地位&#xff0c;是工农业生产的重要组成数据。温湿度过高会造成粮食发霉长芽&#xff0c;还会引起大棚蔬菜一系列的病害。因此对其适时准确的测量就显得尤为重要。而一般的测量过程较为复杂繁琐&#xff0c;误差还大。比如现在所使…

MySQL-SQL存储过程/触发器详解(上)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

Python 循环与判断(详解)

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 前言 在Python中&#xff0c;循环语句有两个&#xff0c;一个是fo…

Redis缓存穿透-击穿-雪崩详细分析加解决办法

Redis 缓存穿透 问题描述-如图 缓存穿透的原因 key 对应的数据在数据源并不存在&#xff0c;每次针对此key 的请求从缓存获取不到&#xff0c;请求都会压到数据源, 可能压垮数据源比如: 用一个不存在的用户id 获取用户信息&#xff0c;不论缓存还是数据库都没有&#xff0c;…

Spring Boot 集成 Redisson分布式锁(拿来即用版)

Redisson 是一种基于 Redis 的 Java 驻留集群的分布式对象和服务库&#xff0c;可以为我们提供丰富的分布式锁和线程安全集合的实现。在 Spring Boot 应用程序中使用 Redisson 可以方便地实现分布式应用程序的某些方面&#xff0c;例如分布式锁、分布式集合、分布式事件发布和订…

JSON5的作用、安装及使用

JSON5是对JSON的扩展&#xff0c;让人可以更容易手工编写和维护&#xff0c;用来减少一些JSON的限制&#xff0c;诸如json语法不支持注释&#xff0c;不支持字符串换行&#xff0c;所有的key都必须双引号&#xff0c;末尾不能有多余的逗号…等等&#xff0c;一大堆极其严格的要…

chatgpt赋能python:Python的退役与SEO

Python的退役与SEO 随着Python编程语言的流行和普及&#xff0c;越来越多的人开始使用它来开发各种类型的应用程序。但是&#xff0c;就像我们所知道的&#xff0c;所有技术都会发生变化&#xff0c;包括编程语言。因此&#xff0c;Python程式员可能会感到困惑和担忧&#xff…