C# 异步

本文详细解释了异步与线程的概念区别,介绍了C#中同步与异步调用的区别,并深入探讨了BeginInvoke、EndInvoke和AsyncCallback在异步调用中的应用。通过实例分析,展示了如何有效利用异步调用提高程序执行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么要用异步

简单说异步就是并行执行两件事,比同步执行快

函数a执行3秒,函数b执行5秒,如果按顺序执行需要8秒(同步)

使用异步的话,可以用异步先调用函数b,然后正常调用函数a,这时b还没执行完,a已经开始执行了,所以总时间应该会<=8秒


什么时候用异步,什么时候用线程

http://blog.csdn.net/onafioo/article/details/44342775


BeginInvoke,EndInvoke,Invoke参数说明

public <方法返回值类型> invoke(<输入和输出变量>)

同步调用,参数就是委托函数的参数可以是多个参数


public  IAsyncResult BeginInvoke( <输入和输出变量>, AsyncCallbackcallback result,  object asyncState) 

启动异步调用,参数就是委托函数的参数,最后两个参数是异步回调函数,和一个object(这个object就是AsyncCallbackcallback 回调函数的参数IAsyncResult


public <方法返回值类型>EndInvoke(<声明为ref或out的参数>, IAsyncResult result )

用于检索异步调用结果,如果异步调用未结束就调用,那么就会阻塞等待异步调用结束,如果不调用这个EndInvoke那么就不会回收BeginInvoke启动的线程,造成资源浪费

所以最好在异步结束后调用EndInvoke


    public interface IAsyncResult
    {
        // 摘要:
        //     获取用户定义的对象,它限定或包含关于异步操作的信息。
        //
        // 返回结果:
        //     用户定义的对象,它限定或包含关于异步操作的信息。
        object AsyncState { get; }
        //
        // 摘要:
        //     获取用于等待异步操作完成的 System.Threading.WaitHandle。
        //
        // 返回结果:
        //     用于等待异步操作完成的 System.Threading.WaitHandle。
        WaitHandle AsyncWaitHandle { get; }
        //
        // 摘要:
        //     获取一个值,该值指示异步操作是否同步完成。
        //
        // 返回结果:
        //     如果异步操作同步完成,则为 true;否则为 false。
        bool CompletedSynchronously { get; }
        //
        // 摘要:
        //     获取一个值,该值指示异步操作是否已完成。
        //
        // 返回结果:
        //     如果操作完成则为 true,否则为 false。
        bool IsCompleted { get; }
    }


同步调用

public delegate void TestDele(int num);
public void showNum(int num)
{
	for(int i=0;i<10000000;i++)
	{
	}
	Debug.Log(string.Format("num:{0}",num));
}
void main(){
	TestDele testDele = new TestDele(showNum);
	testDele.Invok(100);
		
}


普通异步调用

首先委托其实不单是个函数指针,实际是个类,类中有几个方法可用来实现异步BeginInvok,EndInvok等

http://blog.csdn.net/onafioo/article/details/44354219

public delegate void TestDele(int num);
public void showNum(int num)
{
	for(int i=0;i<10000000;i++)
	{
	}
	Debug.Log(string.Format("num:{0}",num));
}
void main(){
	TestDele testDele = new TestDele(showNum);//这里定义委托时,内部自动生成BeginInvok,EndInvok等,这些方法用来实现异步
	testDele.BeginInvok(100);
	Debug.Log("start");
	testDele.EndInvok();//这里必须结束,因为异步实际是从线程池中取了一个空闲线程,用完得归还
	Debug.Log("over");	
}

1 执行testDele.BeginInvok

2 不用等待showNum结束马上执行Debug.Log("start")

3 等待showNum结束执行testDele.EndInvok();//没结束就阻塞

4 最后执行Debug.Log("over");


包含回调的异步调用,AsyncCallback(异步委托)

首先c#异步函数是启动(BeginInvok)了一个线程池中的线程,所以要在函数结束后归还给线程池(EndInvok)

所以问题来了怎么判断函数结束呢

有4种方法http://blog.csdn.net/onafioo/article/details/44354219

而AsyncCallback就是用来在异步函数结束后,使用回调的方法执行EndInvok

用回调的好处

1 不会形成阻塞

2 不用自己循环等待函数结束再EndInvok,等函数结束后自动通过AsyncCallback调用  处理后事的函数执行EndInvok

public delegate void TestDele(int num);
public void showNum(int num){}
void main(){
  TestDele testDele = new TestDele(showNum);//这里定义委托时,内部自动生成BeginInvok,EndInvok等,这些方法用来实现异步
  testDele.BeginInvok(100,new AsynvCallback(callBack),testDele);
  //testDele.BeginInvok(100,new AsynvCallback(callBack),null);//不含扩展参数
}

public void callBack(IAsyncResult ar)//用来处理后事(EndInvok)的函数,参数必须写IAsyncResult类型
{
	TestDele  dele =  (TestDele) ar.AsyncState;
	//TestDele  dele = (TestDele)((AsyncResult)ar).AsyncDelegate;//没扩展参数时也可以获取到发出异步调用的委托 
	dele.EndInvok();
	//这里也可以写点其他有用的东西..
}

简易写法

Action<object> action=(obj)=>method(obj);  
action.BeginInvoke(obj,ar=>action.EndInvoke(ar),null);  


其他异步的例子

上传项目不支持Firefox,提示代码附件太大(1.4M),我写了30多分钟的描述全没了,太坑爹了。 10分有点贵,绝对原创,共2个代码文件300多行,下载请谨慎。你下载了,若绝对不爽在评论中说出来,不要让其他同学上当,如果觉得还可以也请留言。 代码采用多工作者多线程执行任务。通过暴露的方法往工作者传递消息,然后采用事件回调返回处理结果,实现的事件有OnThreadComplete,OnAddedTask,OnStart,OnSuccess,OnFailure,OnTimeout。 事件回调支持同步或异步,每工作者可以指定执行超时时间,避免线程阻塞死掉。队列采用线程安全的BlockingCollection,每组工作者用一个队列。委托采用Func来定义的,没有采用传统且不太好理解的Delegate。这让代码减少很多,也更容易理解。多线程应该采用消息中心来交换数据,这样就规避了线程同步交互,等待,阻塞等等,全部是异步调用,全部是接收消息工作,然后产生消息,线程间没有耦合,消息中心有很多成熟的方案如RabbitMQ, Redis(里面有简单的消息交换),微软有消息云服务等。如果应用不复杂,可以采用DB做个简单的消息中心,建议采用HTTP接口来获取与写入消息,方便将来升级重构消息中心。 开发环境VS2012,Framework4.0,代码注释量很大,如果你高兴这代码你可以随意蹂躏,如果你有建设性意见请告诉我。 下面是部分测试代码: //发送消息方法容器 var msgContainer = new Hashtable(); //创建并启动工作者 foreach (var key in workers.Keys) { //创建工作者 //启动5个线程,异步事件回调,方法执行20秒超时,程序跑起来有100个线程,由于引入超时控制,实际线程将达100+50 //下面的20个工作组,有5个是超时的,主要测试OnTimeout事件,你可以设置seleep的时间来控制 //我把sleep的时间设置的有点长,方便你测试 //测试的时候你会看见有异常,那是应为Timeout我采用的是Thread.Abort方法,这样才出发了ontimeout事件 var worker = new Sehui.Worker(5, key.ToString(), (Func)workers[key], false, new TimeSpan(0, 0, 20)); worker.OnStart += worker_OnEvent; worker.OnSuccess += worker_OnEvent; worker.OnFailure += worker_OnEvent; worker.OnTimeout += worker_OnEvent; //启动工作者 worker.Start(); //将增加消息方法放到Hashtable中 //这里我是偷懒,下面可以用循环的方式往线程中add message msgContainer.Add(key.ToString(), new Func(worker.AddTask)); } //向20个工作者发送消息,每个工作者发送20条消息 for (var i = 0; i < 20; i++) { for (var k = 0; k < 20; k++) { ((Func)msgContainer["SyncDb" + k])("[Work " + k + "] Message " + i); Console.WriteLine("send msg to worker{0},msgid:{1}", k, i); } }
### C# 异步编程基础 C# 提供了一种简洁的方式来处理异步操作,主要通过 `async` 和 `await` 关键字实现。这种方法使得开发者能够轻松编写非阻塞代码,从而提高应用程序的响应性和性能。 #### 定义与作用 异步编程是一种允许程序执行其他任务而不等待长时间运行的操作完成的技术[^1]。在 C# 中,这种技术通常用于网络请求、文件 I/O 或数据库查询等场景下,以防止主线程被挂起。 #### 基本语法结构 当函数声明为 `async` 时,它可以在内部使用 `await` 来暂停其执行直到某个异步过程结束。下面是一个简单的例子展示如何定义并调用一个异步方法: ```csharp public async Task DoWorkAsync() { Console.WriteLine("Start work..."); await Task.Delay(2000); // Simulate an asynchronous operation that takes 2 seconds Console.WriteLine("End work after delay."); } ``` 在此示例中,`Task.Delay` 方法模拟了一个耗时的任务,在此期间不会阻止 UI 线程或其他重要进程继续工作。 #### 实际应用案例 考虑这样一个实际应用场景——从 Web API 获取数据并将结果显示给用户界面而无需冻结整个窗口体验: ```csharp private async void FetchDataButton_Click(object sender, EventArgs e) { string result = await DownloadStringAsync(new Uri("http://example.com")); DisplayResult(result); } private static async Task<string> DownloadStringAsync(Uri address) { using (HttpClient client = new HttpClient()) { return await client.GetStringAsync(address); } } ``` 这里展示了点击按钮事件触发下载字符串的过程,并且由于采用了异步模式,即使网络延迟也不会影响到 GUI 的流畅度。 #### 最佳实践建议 为了充分利用 C#异步功能,遵循一些推荐的最佳做法非常重要: - 避免在库代码中使用同步转异步的方法(如 `.Wait()` 或 `.Result`),这可能导致死锁情况发生。 - 总是以一致的方式命名您的异步方法;一般惯例是在常规名称后面加上 “Async”,比如上面提到的 `DownloadStringAsync` 函数名。 - 对异常情况进行妥善管理,因为它们可能发生在不同的线程上并且行为不同于标准同步错误捕捉机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值