在我开始回答之前,我想解决这个问题引入的一些歧义:
WCF,或者更具体地说,承载 WCF 的 Web 应用程序,将创建一个新的服务实例来并发执行任何服务调用,而不管在客户端上创建服务使用者类时是否生成了异步方法。异步方法只是为了客户端的利益,因此可以进行服务调用,客户端可以继续执行,然后可以在以后的任意时间请求调用的结果。(通常,您会执行 MyService.BeginMyMethod 调用,稍后再执行 MyService.EndMyMethod。)
现在为了解决这个问题,我将采用更简单的方法,并假设您希望 WCF 主机跟踪其当前长时间运行的方法。为了让服务了解其长期运行的方法,您只需维护自己的静态列表即可。一个简单的代码示例:
public class Service1 : IService1
{
public static List<Task> MyRunningTasks = new List<Task>();
private static object MyRunningTasksLockObject = new object();
public void StartMyLongRunningMethod()
{
var myTracker = new MyProcessTrackingClass()
{
OwnerName = HttpContext.Current.User.Identity.Name,
StartTime = DateTime.Now
};
var myAction = new Action<object>(userState =>
{
var myActionTracker = (MyProcessTrackingClass)userState;
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(TimeSpan.FromMinutes(1.0));
myActionTracker.PercentComplete += 10M;
}
});
var myTask = Task.Factory.StartNew(myAction, myTracker, TaskCreationOptions.LongRunning);
AddLongRunningMethod(myTask);
myTask.ContinueWith(t => RemoveLongRunningMethod(t));
}
private static void AddLongRunningMethod(Task item)
{
lock (MyRunningTasksLockObject)
{
MyRunningTasks.Add(item);
}
}
private static void RemoveLongRunningMethod(Task item)
{
lock (MyRunningTasksLockObject)
{
MyRunningTasks.Remove(item);
}
}
}
public sealed class MyProcessTrackingClass
{
public string OwnerName { get; set; }
public DateTime StartTime { get; set; }
public Decimal PercentComplete { get; set; }
}
上面示例中的主体myAction
是您的方法逻辑所在的位置(或者您可以从该点调用一个单独的方法)。此外,这种方法还提供了一些其他好处。一方面,服务方法本身只是创建一个Task
将完成所有繁重工作的方法,因此该方法实际上会快速向客户端返回响应,因此无论您在客户端创建异步方法还是创建异步方法都没有关系不是。
另一个好处是,由于您的服务知道正在运行的进程,您甚至可以公开另一个允许客户端查询当前长时间运行的进程的服务方法,如下所示:
public IEnumerable<MyProcessTrackingClass> ListLongRunningProcesses()
{
return MyRunningTasks.Select(t => (MyProcessTrackingClass)t.AsyncState);
}
正如在最后一个示例中所指出的,AsyncState
每个属性都Task
包含我们的自定义类,作为state
创建. 您可以更新以包含您希望与运行方法关联的任何属性。StartNew
Task
MyProcessTrackingClass