异步编程
异步编程的三种模式
TAP模式(Task-based Asynchronous Pattern )
tsak 异步编程使用
- 当程序中有大量I/O操作(如:读写数据库、上传或下载数据、读取或写入文件)等操作时,可以考虑使用异步
- 当程序中耗时的操作时,可以使用task.run()方式,在后台线程中调用耗时方法。
APM模式(Asynchronous-Programming-Model)
EAP模式(Event-based Asynchronous Pattern)
1.介绍
- 对于简单的异步任务,直接调用BackgroundWorker 组件就可以了。对于复杂的异步任务,可以通过封装类使用EAP模式,异步调用任务。
- client 端使用event/delegate 模式来管理异步任务
对要异步执行的方法,这方法所在类定义一个event,通知client 端
public event MethodNameCompletedEventHandler MethodNameCompleted;
在这个类的同一个namespace 定义封装事件的Delegate 和参数(AsyncCompletedEventArgs.)
public delegate void MethodNameCompletedEventHandler(object sender,
MethodNameCompletedEventArgs e);
public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
}
异步方法与事件参数要一致,例如:
public void MethodNameAsync(string arg1, string arg2);//异步方法
public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
public int Result { get; };
public string Arg2 { get; };
public string Arg3 { get; };
}
- 可以依据业务需要,添加取消异步任务,更新异步任务进度等方法。
2. 使用
可以先封装一个支持EAP的组件,然后调用组件,调用异步任务。
msdn中代码示例
- 代码中封装的组件主要有两个通知client的事件,一个更新通知事件,一个完成事件,异步方法是计算某个随机数是否为素数。当多次调用异步方法时,对于每次的调用分配唯一的taskID来标识,并使用HybridDictionary的集合管理task ID
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading;
namespace EAPTset01
{
public delegate void ProgressChangedEventHandler(
ProgressChangedEventArgs e);
public delegate void CalculatePrimeCompletedEventHandler(
object sender,
CalculatePrimeCompletedEventArgs e);
public class PrimeNumberCalculator : Component
{
//调用异步方法的委托
private delegate void WorkerEventHandler(
int numberToCheck,
AsyncOperation asyncOp);
private SendOrPostCallback onProgressReportDelegate;//进度更新
private SendOrPostCallback onCompletedDelegate;//
private HybridDictionary userStateToLifetime =
new HybridDictionary();//管理task的集合,每个task有唯一的task ID
private System.ComponentModel.Container components = null;
/
#region 外部通知事件
public event ProgressChangedEventHandler ProgressChanged;
public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
#endregion
/
#region 构造函数
public PrimeNumberCalculator(IContainer container)
{
container.Add(this);
InitializeComponent();
InitializeDelegates();
}
public PrimeNumberCalculator()
{
InitializeComponent();
InitializeDelegates();
}
protected virtual void InitializeDelegates()
{
onProgressReportDelegate =
new SendOrPostCallback(ReportProgress);
onCompletedDelegate =
new SendOrPostCallback(CalculateCompleted);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#endregion // Construction and destruction
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
/
///
#region 耗时方法实现
// This method starts an asynchronous calculation.
// First, it checks the supplied task ID for uniqueness.
// If taskId is unique, it creates a new WorkerEventHandler
// and calls its BeginInvoke method to start the calculation.
public virtual void CalculatePrimeAsync(
int numberToTest,
object taskId)
{
// Create an AsyncOperation for taskId.
AsyncOperation asyncOp =
AsyncOperationManager.CreateOperation(taskId);
// Multiple threads will access the task dictionary,
// so it must be locked to serialize access.
lock (userStateToLifetime.SyncRoot)
{
if (userStateToLifetime.Contains(taskId))
{
throw new ArgumentException(
"Task ID parameter must be unique",
"taskId");
}
userStateToLifetime[taskId] = asyncOp;
}
// Start the asynchronous operation.开启异步调用
WorkerEventHandler workerDelegate = new WorkerEventHandler(CalculateWorker);
workerDelegate.BeginInvoke(
numberToTest,
asyncOp,
null,
null);
}
// Utility method for determining if a
// task has been canceled.
private bool TaskCanceled(object taskId)
{
return (userStateToLifetime[taskId] == null);
}
// This method cancels a pending asynchronous operation.
//取消一个异步任务
public void CancelAsync(object taskId)
{
AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
if (asyncOp != null)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(taskId);
}
}
}
// This method performs the actual prime number computation.
// It is executed on the worker thread.
private void CalculateWorker(
int numberToTest,
AsyncOperation asyncOp)
{
bool isPrime = false;
int firstDivisor = 1;
Exception e = null;
// Check that the task is still active.
// The operation may have been canceled before
// the thread was scheduled.
if (!TaskCanceled(asyncOp.UserSuppliedState))
{
try
{
// Find all the prime numbers up to
// the square root of numberToTest.
ArrayList primes = BuildPrimeNumberList(
numberToTest,
asyncOp);
// Now we have a list of primes less than
// numberToTest.
isPrime = IsPrime(
primes,
numberToTest,
out firstDivisor);
}
catch (Exception ex)
{
e = ex;
}
}
this.CompletionMethod(
numberToTest,
firstDivisor,
isPrime,
e,
TaskCanceled(asyncOp.UserSuppliedState),
asyncOp);
}
#region 耗时的方法
// This method computes the list of prime numbers used by the
// IsPrime method.
private ArrayList BuildPrimeNumberList(
int numberToTest,
AsyncOperation asyncOp)
{
ProgressChangedEventArgs e = null;
ArrayList primes = new ArrayList();
int firstDivisor;
int n = 5;
// Add the first prime numbers.
primes.Add(2);
primes.Add(3);
// Do the work.
while (n < numberToTest &&
!TaskCanceled(asyncOp.UserSuppliedState))
{
if (IsPrime(primes, n, out firstDivisor))
{
// Report to the client that a prime was found.
e = new CalculatePrimeProgressChangedEventArgs(
n,
(int)((float)n / (float)numberToTest * 100),
asyncOp.UserSuppliedState);
asyncOp.Post(this.onProgressReportDelegate, e);
primes.Add(n);
// Yield the rest of this time slice.
Thread.Sleep(0);
}
// Skip even numbers.
n += 2;
}
return primes;
}
// This method tests n for primality against the list of
// prime numbers contained in the primes parameter.
private bool IsPrime(
ArrayList primes,
int n,
out int firstDivisor)
{
bool foundDivisor = false;
bool exceedsSquareRoot = false;
int i = 0;
int divisor = 0;
firstDivisor = 1;
// Stop the search if:
// there are no more primes in the list,
// there is a divisor of n in the list, or
// there is a prime that is larger than
// the square root of n.
while (
(i < primes.Count) &&
!foundDivisor &&
!exceedsSquareRoot)
{
// The divisor variable will be the smallest
// prime number not yet tried.
divisor = (int)primes[i++];
// Determine whether the divisor is greater
// than the square root of n.
if (divisor * divisor > n)
{
exceedsSquareRoot = true;
}
// Determine whether the divisor is a factor of n.
else if (n % divisor == 0)
{
firstDivisor = divisor;
foundDivisor = true;
}
}
return !foundDivisor;
}
#endregion
// This method is invoked via the AsyncOperation object,
// so it is guaranteed to be executed on the correct thread.
private void CalculateCompleted(object operationState)
{
CalculatePrimeCompletedEventArgs e =
operationState as CalculatePrimeCompletedEventArgs;
//通知 client 端,异步任务已完成
CalculatePrimeCompleted?.Invoke(this, e);
}
// This method is invoked via the AsyncOperation object,
// so it is guaranteed to be executed on the correct thread.
private void ReportProgress(object state)
{
ProgressChangedEventArgs e =
state as ProgressChangedEventArgs;
//通知client 端,进度有变化
ProgressChanged?.Invoke(e);
}
protected void OnCalculatePrimeCompleted(
CalculatePrimeCompletedEventArgs e)
{
if (CalculatePrimeCompleted != null)
{
CalculatePrimeCompleted(this, e);
}
}
protected void OnProgressChanged(ProgressChangedEventArgs e)
{
if (ProgressChanged != null)
{
ProgressChanged(e);
}
}
// This is the method that the underlying, free-threaded
// asynchronous behavior will invoke. This will happen on
// an arbitrary thread.
private void CompletionMethod(
int numberToTest,
int firstDivisor,
bool isPrime,
Exception exception,
bool canceled,
AsyncOperation asyncOp)
{
// If the task was not previously canceled,
// remove the task from the lifetime collection.
if (!canceled)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(asyncOp.UserSuppliedState);
}
}
// Package the results of the operation in a
// CalculatePrimeCompletedEventArgs.
CalculatePrimeCompletedEventArgs e =
new CalculatePrimeCompletedEventArgs(
numberToTest,
firstDivisor,
isPrime,
exception,
canceled,
asyncOp.UserSuppliedState);
// End the task. The asyncOp object is responsible
// for marshaling the call.
// 结束异步操作,调用回调方法
asyncOp.PostOperationCompleted(onCompletedDelegate, e);
// Note that after the call to OperationCompleted,
// asyncOp is no longer usable, and any attempt to use it
// will cause an exception to be thrown.
}
#endregion
}
/// ///
#region 封装事件参数
public class CalculatePrimeProgressChangedEventArgs :
ProgressChangedEventArgs
{
private int latestPrimeNumberValue = 1;
public CalculatePrimeProgressChangedEventArgs(
int latestPrime,
int progressPercentage,
object userToken) : base(progressPercentage, userToken)
{
this.latestPrimeNumberValue = latestPrime;
}
public int LatestPrimeNumber
{
get
{
return latestPrimeNumberValue;
}
}
}
public class CalculatePrimeCompletedEventArgs :
AsyncCompletedEventArgs
{
private int numberToTestValue = 0;
private int firstDivisorValue = 1;
private bool isPrimeValue;
public CalculatePrimeCompletedEventArgs(
int numberToTest,
int firstDivisor,
bool isPrime,
Exception e,
bool canceled,
object state) : base(e, canceled, state)
{
this.numberToTestValue = numberToTest;
this.firstDivisorValue = firstDivisor;
this.isPrimeValue = isPrime;
}
public int NumberToTest
{
get
{
// Raise an exception if the operation failed or
// was canceled.
RaiseExceptionIfNecessary();
// If the operation was successful, return the
// property value.
return numberToTestValue;
}
}
public int FirstDivisor
{
get
{
// Raise an exception if the operation failed or
// was canceled.
RaiseExceptionIfNecessary();
// If the operation was successful, return the
// property value.
return firstDivisorValue;
}
}
public bool IsPrime
{
get
{
// Raise an exception if the operation failed or
// was canceled.
RaiseExceptionIfNecessary();
// If the operation was successful, return the
// property value.
return isPrimeValue;
}
}
}
#endregion
}
运行界面
3. 代码下载地址
完整代码下载地址 :download