在现代软件开发中,异步编程已成为处理并发任务的一种常见方法。它允许程序在等待长时间运行的操作(如数据库访问或网络通信)时继续执行其他任务。然而,异步编程往往伴随着复杂的代码结构和错误处理机制。本文将介绍一种简化异步编程模式的方法,通过使用任务并行库(TPL)和回调函数来提高代码的清晰度和可维护性。
在旧模式中,通常使用后台工作线程(BackgroundWorker)来处理异步调用。这种方法虽然有效,但代码往往冗长且难以维护。以下是一个典型的后台工作线程代码示例:
void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
e.Result = _iBLLItemCategory.Select(Convert.ToInt32(e.Argument));
}
void BackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
if (e.Error == null) {
if (e.Result != null) {
this.ic_ItemCategory = (ic_ItemCategory)e.Result;
} else {
_iViewModelUIService.MessageDialog("Data Error", "Record Not Found", string.Format("Item Category key: {0} not found", _objLoadKey.ToString));
}
} else {
_objIViewModelUIService.ExceptionDialog(e.Error);
}
}
在这段代码中,可以看到后台工作线程的启动、异常检查、业务层调用、成功结果代码路径和异常结果代码路径。虽然这种模式被广泛接受,但它的复杂性和冗长性促使寻找更好的解决方案。
新模式将上述代码简化为一行代码,同时保留了旧模式的所有优点。新模式的关键在于使用回调函数。回调函数允许在异步操作成功或失败时执行特定的操作。以下是一个新模式的示例:
public interface IItemRepository {
void GetAll(Action> resultCallback, Action errorCallback);
void Get(Int32 itemId, Action- resultCallback, Action
errorCallback);
}
在这个新模式中,定义了一个接口IItemRepository,它包含两个方法:GetAll和Get。这两个方法都接受两个回调函数作为参数:resultCallback和errorCallback。当异步操作成功时,resultCallback被调用;当异步操作失败时,errorCallback被调用。这种模式极大地简化了代码,并提高了可读性和可维护性。
新模式的实现依赖于任务并行库(TPL)。TPL是.NET Framework 4中引入的一个库,它提供了一种简化并行编程的方法。以下是一个使用TPL实现新模式的示例:
[Export(typeof(IItemRepository))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ItemRepository : IItemRepository {
readonly ThePhoneCompanyEntities _dataService;
[ImportingConstructor]
public ItemRepository(DataServiceFacade dataServiceFacade) {
_dataService = dataServiceFacade.DataService;
}
void IItemRepository.GetAll(Action> resultCallback, Action errorCallback) {
Task>> task = Task.Factory.StartNew(() => {
try {
return new RepositoryResult>(_dataService.Items.ToList(), null);
} catch (Exception ex) {
return new RepositoryResult>(null, ex);
}
});
task.ContinueWith(r => {
if (r.Result.Error != null) {
errorCallback(r.Result.Error);
} else {
resultCallback(r.Result.Package);
}
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
void IItemRepository.Get(Int32 itemId, Action- resultCallback, Action
errorCallback) {
if (itemId != 0) {
Task> task = Task.Factory.StartNew(() => {
try {
return new RepositoryResult- (_dataService.Items.Where(i => i.ItemID == itemId).FirstOrDefault(), null);
} catch (Exception ex) {
return new RepositoryResult
- (null, ex);
}
});
task.ContinueWith(r => {
if (r.Result.Error != null) {
errorCallback(r.Result.Error);
} else {
resultCallback(r.Result.Package);
}
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
} else {
resultCallback(((IItemRepository)this).Create());
}
}
Item IItemRepository.Create() {
return new Item();
}
}
在这个实现中,使用了TPL的Futures模式。这种模式允许在后台线程中执行异步操作,并在操作完成后在调用线程上调用回调函数。这种实现方式不仅简化了代码,还提高了性能。
1. 实际执行工作的代码被包装在try-catch块中,以处理可能发生的异常。 2. 异步操作的结果被包装在一个RepositoryResult对象中。 3. RepositoryResult用于在调用线程上调用resultCallback或errorCallback。 4. TaskScheduler.FromCurrentSynchronizationContext方法调用确保两个回调在调用线程上被调用。