WCF Rest 服务中的后台工作

Background work in WCF Rest Service

我有一个 WCF Rest 服务公开了一个 Web 方法,该方法应该启动一个长 运行 进程,然后立即 return 一个表示可用于跟踪状态的任务的 ID任务。

[WebGet]
public Task<Guid> LongRunningProcess()
{
    var taskId = new Guid();

    var task = Task.Factory.StartNew(() =>
    {
        //Perform long running task
    }

    task.ContinueWith(task =>
    {
        //Send a notification to the client that the task has completed.
    }

    return taskId;
}

我的问题是,这是正确的做法吗?还是有更好更轻量级的方法?

您所勾勒的内容(稍作修改)是实现您想要做的事情的方法。更难的部分是客户端的通知(我们使用 SignalR hubs 成功地做到了这一点,但具体的机制取决于你)。

我所说的小修正是你的方法的 return 类型在上面的代码中应该只是 Guid

一些注意事项: 在性能方面,TPL 可以很好地扩展(IMO),但在更大的范围内,您可能希望能够将 运行 长的任务分配到多个服务器等...

对于这种情况,我建议您查看非常适合此类用例的分布式作业队列(例如 Resque,存在 .NET 端口)。

我的理解是,如果你的工作是 CPU 绑定的,你最好同步执行工作。使用您的方法,请求将被停放并且原始请求线程将被释放,但随后将工作移交给另一个线程,并且在该线程完成之前请求不会完成。您不妨在原帖中完成工作。

如果你在那里有一些 IO,那么使异步 IO 不使用线程是有意义的,它会释放你的请求线程来处理其他请求,从而提高你的可伸缩性。

更新

我认为您采用的方法很好,但考虑到您使用的是 .NET 4.5,我会使用 async-await,因为它会产生更简单的代码。然后我会使用异步 API IO 操作和 await 它的结果。例如:

[WebGet]
public async Task<Guid> LongRunningProcess()
{
    var taskId = new Guid();

    // IO bound operation
    var dbResult = await readFromDbAsync();

    // IO bound operation
    var dbResult = await readFromDbAsync();

    // CPU bound?
    generateReport(dbResult);

    // IO bound operation
    await sendNotification();

    return taskId;
}

如果你不熟悉async-await,我已经写了一篇介绍here