C#中异步回调函数用法实例
在C#异步编程中,利用委托的 BeginInvoke 配合 AsyncCallback 可实现非阻塞的任务执行与结果回传。本文将通过具体代码演示如何发起异步调用、检测状态以及通过回调函数安全获取返回值。
异步回调的实现原理
C#中的异步委托允许将耗时操作(如I/O密集型任务或长时间计算)放入线程池执行,防止主线程(通常是UI线程或请求处理线程)被阻塞。通过 BeginInvoke 方法,我们可以传入回调函数,当异步任务结束时,该回调将在后台线程自动执行,从而通知调用者任务已完成。
完整操作代码示例
以下代码展示了一个完整的异步委托使用场景:定义一个异步委托,设置回调函数,并在等待期间模拟主线程进行其他操作。
static void Main(string[] args)
{
// 1. 定义代理委托,指向同步方法 ShowMessage
Func<string,string> showMessage = ShowMessage;
// 2. 启动异步操作
// 参数说明:
// - "测试异步委托":传递给 ShowMessage 的参数
// - new AsyncCallback(Completed):异步完成时执行的回调方法
// - null:用户自定义对象,用于向回调传递额外状态(此处未使用)
IAsyncResult result = showMessage.BeginInvoke("测试异步委托",new AsyncCallback(Completed),null);
// 3. 主线程非阻塞等待
while(!result.IsCompleted)
{
Console.WriteLine("主线程可以进行其它的操作!");
Thread.Sleep(100); // 建议加入短暂休眠以减少CPU占用
}
Console.ReadLine();
}
// 被调用的同步方法(将在后台线程执行)
static string ShowMessage(string x)
{
string current = string.Format("当前线程id为{0}",Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(3000); // 模拟耗时操作
return string.Format("{0},输入为{1}", current, x);
}
// 异步完成后的回调方法
static void Completed(IAsyncResult result)
{
Console.WriteLine("异步完成!");
// 4. 在回调中获取返回值
// 注意:必须在回调中或主线程中调用 EndInvoke 以释放资源并获取结果
AsyncResult _result = (AsyncResult) result;
Func<string, string> showMessage = (Func<string, string>) _result.AsyncDelegate;
// 结束异步操作并输出结果
Console.WriteLine(showMessage.EndInvoke(_result));
}
关键步骤解析
第1步:定义委托与方法
首先定义一个符合签名的委托(如 Func<string, string>),并将具体的业务逻辑封装在同步方法(如 ShowMessage)中。即使业务逻辑是异步的,使用委托模式也能统一入口。
第2步:调用 BeginInvoke
使用委托变量的 BeginInvoke 方法启动异步执行。第二个参数传入 AsyncCallback 委托,指向完成后需要执行的方法。此时方法立即返回 IAsyncResult 对象,主线程控制权交还,可以继续执行后续逻辑。
第3步:监测状态与等待(可选)
示例中通过轮询 result.IsCompleted 来确保在主线程不退出前看到结果。在实际应用中,如果使用回调机制,通常不需要在主线程显式等待,因为 EndInvoke 应在回调内部或通过其他方式调用。若需同步等待,可使用 result.AsyncWaitHandle.WaitOne()。
第4步:在回调中获取结果
这是易错点。Completed 方法本身没有返回值,因此无法直接返回 ShowMessage 的结果。必须通过 IAsyncResult 获取原始的委托对象(_result.AsyncDelegate),然后强制转换回原来的委托类型,再次调用 EndInvoke。 EndInvoke 的作用是:
- 阻塞直到异步操作真正结束(如果在
BeginInvoke后立即调用); - 捕获异步操作中抛出的异常;
- 获取方法的返回值。
注意事项与排错
- 线程上下文切换:回调函数
Completed运行在CLR线程池线程上,而非主线程。如果需要更新UI控件(如WinForms/WPF),必须使用Control.Invoke或Dispatcher.Invoke将操作封送回UI线程,否则可能抛出跨线程操作异常。 - 内存泄漏:务必对每个
BeginInvoke调用对应的EndInvoke,无论成功还是失败。未调用EndInvoke会导致内存和资源泄漏。 - 参数传递:如果需要在回调中传递复杂数据,可以通过
BeginInvoke的第四个参数state传入一个包含所需信息的对象,并在回调中通过(MyStateClass)result.AsyncState获取。
以上就是C#中异步回调函数用法的详细内容,更多关于C#异步编程的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













