C#技巧之同步与异步

news2024/9/28 9:33:28

区别

首先,同步就是程序从上往下顺序执行,要执行完当前流程,才能往下个流程去。

而异步,则是启动当前流程以后,不需要等待流程完成,立刻就去执行下一个流程。


同步示例

创建一个窗体,往窗体里面添加一个button,写入以下代码。

private void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 5; i++)
    {
        method($"button1_click{i}");
    }
    
}

 private void method(string str)
 {
     Console.WriteLine($"{str}在线程{Thread.CurrentThread.ManagedThreadId.ToString()}启动,时间{DateTime.Now.Second.ToString()}:{DateTime.Now.Millisecond.ToString()}");
     for (var i = 0; i < 1000; i++)
     {
         Thread.Sleep(1);
     }
     Console.WriteLine($"{str}线程{Thread.CurrentThread.ManagedThreadId.ToString()}结束,时间{DateTime.Now.Second.ToString()}:{DateTime.Now.Millisecond.ToString()}");
     Console.WriteLine("--------------------------------------");
 }

如果所示,当点击按钮以后,会五次执行method方法,而method方法就是一个从0-999再数数,然后打印启动时间和结束时间。我们看看执行结果。

button1_click0在线程1启动,时间16:239
button1_click0线程1结束,时间18:229
--------------------------------------
button1_click1在线程1启动,时间18:229
button1_click1线程1结束,时间20:209
--------------------------------------
button1_click2在线程1启动,时间20:209
button1_click2线程1结束,时间22:195
--------------------------------------
button1_click3在线程1启动,时间22:195
button1_click3线程1结束,时间24:182
--------------------------------------
button1_click4在线程1启动,时间24:182
button1_click4线程1结束,时间26:169
--------------------------------------

可以看到,button_click是一一对应的,也即是说当click0启动后,要等0完成,才能启动click1,并且,这五个循环,都是在主线程1中执行,这就会导致一个问题,因为控件的创建也是在主线程1中,如果线程1被拿来执行一些长耗时的工作,那么窗体上的控件就会卡住。下面我们再看看异步示例。再创建个button2,添加以下代码。


异步示例

private void button2_Click(object sender, EventArgs e)
{
    Action<string> action = method;
    for (int i = 0;i<5;i++)
    {
        action.BeginInvoke($"button2_click{i}", null, null);
    }
    
}

private void method(string str)
{
    Console.WriteLine($"{str}在线程{Thread.CurrentThread.ManagedThreadId.ToString()}启动,时间{DateTime.Now.Second.ToString()}:{DateTime.Now.Millisecond.ToString()}");
    for (var i = 0; i < 1000; i++)
    {
        Thread.Sleep(1);
    }
    Console.WriteLine($"{str}线程{Thread.CurrentThread.ManagedThreadId.ToString()}结束,时间{DateTime.Now.Second.ToString()}:{DateTime.Now.Millisecond.ToString()}");
    Console.WriteLine("--------------------------------------");
}

同样是执行5次method2,但button2使用的方法是通过委托的异步执行来实现。我们再来看看结果。

button2_click1在线程6启动,时间15:389
button2_click4在线程9启动,时间15:389
button2_click0在线程3启动,时间15:390
button2_click2在线程7启动,时间15:389
button2_click3在线程8启动,时间15:389
button2_click3线程8结束,时间17:369
--------------------------------------
button2_click1线程6结束,时间17:372
--------------------------------------
button2_click4线程9结束,时间17:374
--------------------------------------
button2_click0线程3结束,时间17:374
--------------------------------------
button2_click2线程7结束,时间17:374
--------------------------------------

可以看到,异步执行的话是同时触发五个计数流程,然后通过五个不同的线程来分别进行,所以不仅主线程创建的控件不会卡死,另外,执行时间也比同步要快不少。

但异步会出现一个问题,就是无序,如果有些流程,要先执行完1,再执行2,那么使用上面的方法,就有点难以控制。这时,我们可以使用回调函数,所谓回调函数,就是当异步执行结束以后会执行的函数,这时,只要把下个流程要执行的条件,写在当前流程的回调函数中,那么就可以实现顺序控制了。

private void button2_Click(object sender, EventArgs e)
{
    Action<string> action = method;
    AsyncCallback asyncCallback = method2;
    for (int i = 0;i<5;i++)
    {
        action.BeginInvoke($"button2_click{i}", asyncCallback, null);
    }
    
}

 private void method(string str)
 {
     Console.WriteLine($"{str}在线程{Thread.CurrentThread.ManagedThreadId.ToString()}启动,时间{DateTime.Now.Second.ToString()}:{DateTime.Now.Millisecond.ToString()}");
     for (var i = 0; i < 1000; i++)
     {
         Thread.Sleep(1);
     }
     Console.WriteLine($"{str}线程{Thread.CurrentThread.ManagedThreadId.ToString()}结束,时间{DateTime.Now.Second.ToString()}:{DateTime.Now.Millisecond.ToString()}");
     Console.WriteLine("--------------------------------------");
 }

private void method2(IAsyncResult ia)
{
    Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}执行完成?"+ia.IsCompleted);

}

运行结果:

button2_click0在线程3启动,时间48:919
button2_click1在线程8启动,时间48:919
button2_click2在线程9启动,时间48:919
button2_click3在线程10启动,时间48:921
button2_click4在线程4启动,时间48:922
button2_click0线程3结束,时间50:894
--------------------------------------
线程3执行完成?True
button2_click2线程9结束,时间50:897
--------------------------------------
button2_click1线程8结束,时间50:897
--------------------------------------
线程8执行完成?True
线程9执行完成?True
button2_click3线程10结束,时间50:897
--------------------------------------
线程10执行完成?True
button2_click4线程4结束,时间50:897
--------------------------------------
线程4执行完成?True

可以看到,每个线程执行完以后,都会有反馈,我们可以在反馈函数中写逻辑,这里就不详细叙述了。


控件不在创建线程中被调用

如果控件不在创建线程中被调用,会报错。

创建一个button对象button3和一个label对象lbl,添加以下代码。

 private void button3_Click(object sender, EventArgs e)
 {
     Thread t1 = new Thread(Method3);
     t1.Start();
 }

  private void Method3()
  {
      for (var i = 0; i < 10; i++)
      {
          Thread.Sleep(1000);
          ChangeLabel(label1, i.ToString());
      }
  }

private void ChangeLabel(Label lbl,string str)
{
    lbl.Text = str;
}

如上,点击button3以后,会启动一个分线程,然后在分线程中执行Method3方法,而Method3方法的操作是,每隔一秒,就改变lbl的text属性。当我们按下button3后,会显示如下结果。

要解决这种方法,就可以使用异步调用。代码如下。

private delegate void MyDel(Label lbl,string str);

private void button3_Click(object sender, EventArgs e)
{
    Thread t1 = new Thread(Method3);
    t1.Start();
}

 private void Method3()
 {
     for (var i = 0; i < 10; i++)
     {
         Thread.Sleep(1000);
         ChangeLabel(label1, i.ToString());
     }
 }

private void ChangeLabel(Label lbl,string str)
{
    if (lbl.InvokeRequired)    //如果lbl控件被创建其线程以外的线程调用,那么InvokeRequire为true
    {
        MyDel myDel = ChangeLabel;
        lbl.BeginInvoke(myDel, new object[] { label1, str });    //启动异步调用
    }
    else
    {
        lbl.Text = str;
    }
}

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

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

相关文章

ES数据存储与查询基本原理

Elasticsearch&#xff08;ES&#xff09;简介 Elasticsearch&#xff08;ES&#xff09;是一个分布式、可扩展、近实时的搜索和分析引擎&#xff0c;它基于Lucene&#xff0c;设计用于云计算中&#xff0c;处理大规模文档检索和数据分析任务&#xff0c;常用于实现内部搜索引…

二维泊松方程(Neumann+Direchliet边界条件)有限元Matlab编程求解|程序源码+说明文本

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

详解面向对象-类和对象

1.面向对象与面向过程的区别 ①面向过程 &#xff1a;关注点是在实现功能的步骤上面&#xff0c;就是分析出解决问题所需要的步骤&#xff0c;让后函数把这些步骤一步一步实现&#xff0c;使用的时候一个一个依次调用就可以。对于简单的流程是适合面向过程的方式进行的&#x…

模型全参数训练和LoRA微调所需显存的分析

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

TypeScript 基础学习笔记:泛型 <T> vs 断言 as

TypeScript 基础学习笔记&#xff1a;泛型 <T> vs 断言 as &#x1f525; 引言 &#x1f44b; TypeScript (TS) 以其静态类型的魔力&#xff0c;让我们的代码更加健壮、易读且易于维护。今天&#xff0c;我们将深入探讨两个核心概念——泛型&#xff08;Generics&#x…

使用socket+Python实现ping

import os import socket import struct import select import time# 计算校验和&#xff0c;用于确保数据的完整性 def checksum(source_string):sum 0count 0max_count len(source_string)# 处理成对的字节while count < max_count - 1:val source_string[count 1] *…

结构体介绍(2)

结构体介绍&#xff08;2&#xff09; 前言一、结构体的内存对齐之深入理解为什么存在内存对齐&#xff1f;修改默认对齐数 二、结构体传参2.1&#xff1a;该怎么传参呢&#xff1f; 三、结构体实现位段3.1什么是位段位段的内存分配位段的跨平台问题 总结 前言 根据之前讲了结…

金融行业AI大模型百项应用案例综述【大模型系列】

逐浪金融大模型的玩家&#xff0c;除了BAT、华为等高科技巨头&#xff0c;试图以技术优势充当产业链的“卖铲人”&#xff0c;更多的还是金融和类金融企业&#xff0c;包括银行、保险、互金、券商等&#xff0c;既不想被喧宾夺主&#xff0c;又不想肥水外流&#xff0c;都在押注…

基于Springboot的校园竞赛管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园竞赛管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

VS2019下使用MFC完成科技项目管理系统

背景&#xff1a; &#xff08;一&#xff09;实验目的 通过该实验&#xff0c;使学生掌握windows程序设计的基本方法。了解科技项目组织管理的主要内容和管理方面的基本常识&#xff0c;熟练应用数据库知识&#xff0c;通过处理过程对计算机软件系统工作原理的进一步理解&am…

新型直膨式光伏光热热泵/动力热管复合循环系统

太阳能光伏光热热泵&#xff08;即PVT热泵&#xff09;技术是建筑领域内实现碳中和的有效技术手段&#xff0c;该技术具有优越的热电冷联产能力。然而&#xff0c;现有的PVT热泵在良好的室外工况下能耗较高。为了解决这一问题&#xff0c;本文提出了一种新型的DX-PVT热泵/动力热…

解决网络ping不通问题

网络ping不通可能有多种原因&#xff0c;以下是一些常见的解决方法&#xff1a; 1. 检查IP地址和域名&#xff1a;确保你使用的是正确的IP地址或者域名来ping目标设备。如果IP地址或者域名错误&#xff0c;ping请求将无法到达目标设备。 2. 检查网络连接&#xff1a;首先确保…

【电源专题】拿人体的循环系统与板级电源做个比较

一般人可能会觉得电源大概是电子设备里面比较容易搞定的门类。因为,只要线路没有接错,指示灯(如果有)能亮,电源都能工作。从这个方面说,好像是很容易。但是通过多年的经验和经历的坑,发现电源其实是一个很麻烦的东西,稍微有一点不完美就会有大问题出现。 如果将人体也当…

cloudreve手动添加文件

cloudreve导入本地已有的文件&#xff0c;不需要再次上传 需要更新版本到3.1及更高 在【管理面板】-----【文件】导入 如上图&#xff1a; 选择目标用户、原始路径、目的路径&#xff0c;创建导入任务即可&#xff01;

[C++基础学习-06]----C++指针详解

前言 指针是一个存储变量地址的变量&#xff0c;可以用来访问内存中的数据。在C中&#xff0c;指针是一种非常有用的数据类型&#xff0c;可以帮助我们在程序中对内存进行操作和管理。 正文 01-指针简介 指针的基本概念如下&#xff1a; 声明指针&#xff1a;使用“*”符…

AI+客服行业落地应用

一、客服行业变迁 1.传统客服时代 &#xff08;1&#xff09;客服工作重复性高&#xff0c;技术含量低 &#xff08;2&#xff09;呼出效率低&#xff0c;客服水平参差不齐 &#xff08;3&#xff09;管理难度高&#xff0c;情绪不稳定 &#xff08;4&#xff09;服务质量…

【系统架构师】-选择题(十三)

1、在某企业的营销管理系统设计阶段&#xff0c;属性"员工"在考勤管理子系统中被称为"员工"&#xff0c;而在档案管理子系统中被称为"职工"&#xff0c;这类冲突称为&#xff08; 命名冲突&#xff09;。 同一个实体在同系统中存在不同的命名&am…

2024“天一永安杯“宁波第七届网络安全大赛极安云科战队部分WP

“天一永安杯”2024 宁波第七届网络安全大赛暨第九届大学生网络技术与信息安全大赛 大赛竞赛形式 一、线上初赛 参赛人员&#xff1a;各单位自行选拔3人&#xff08;设队长1名&#xff09;组成团队&#xff0c;不足3人不允许参赛。 竞赛时间&#xff1a;8&#xff1a;30-12&…

15 C语言常用函数

C语言常用函数 本次笔记参考哔站尚硅谷宋红康老师的C语言教程。 C语言是一种广泛应用的编程语言&#xff0c;它提供了一系列的标准库函数&#xff0c;使得程序员能够更高效地编写程序。函数是C语言编程中的基础&#xff0c;通过它们&#xff0c;程序员可以构建出功能丰富的应用…

牛客NC382 切割木头【中等 二分超找 Java/Go/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/707d98cee255448c838c76918a702be0 核心 二分查找Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可…