具有返回值的Hangfire背景作业

时间:2015-03-13 21:54:12

标签: c# asp.net asynchronous hangfire

我正在从Task.Run切换到Hangfire。在.NET 4.5+ Task.Run中可以返回Task<TResult>,这允许我运行返回void以外的任务。我通常可以通过访问属性MyReturnedTask.Result

等待并获得我的任务结果

我的旧代码示例:

public void MyMainCode()
{
    List<string> listStr = new List<string>();
    listStr.Add("Bob");
    listStr.Add("Kate");
    listStr.Add("Yaz");

    List<Task<string>> listTasks = new List<Task<string>>();

    foreach(string str in listStr)
    {
        Task<string> returnedTask = Task.Run(() => GetMyString(str));
        listTasks.Add(returnedTask);
    }

    foreach(Task<string> task in listTasks)
    {
        // using task.Result will cause the code to wait for the task if not yet finished.
        // Alternatively, you can use Task.WaitAll(listTasks.ToArray()) to wait for all tasks in the list to finish.
        MyTextBox.Text += task.Result + Environment.NewLine;
    }
}
private string GetMyString(string str)
{
    // long execution in order to calculate the returned string
    return str + "_finished";
}

据我所知,在Hangfire的Quick Start页面上,您的主要人物为BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget")); 完美地将代码作为后台作业运行,但显然不支持具有返回值的作业(如上面提到的代码)。是对的吗?如果没有,我如何调整我的代码才能使用Hangfire?

P.S。我已经查看了HostingEnvironment.QueueBackgroundWorkItemhere),但它显然缺乏相同的功能(后台工作必须是void

修改

正如@Dejan所知,我想切换到Hangfire的主要原因与.NET人员在.NET 4.5.2中添加QueueBackgroundWorkItem的原因相同。这个原因在Scott Hanselman关于ASP.NET中的后台任务的伟大article中得到了很好的描述。所以我要引用文章:

  

QBWI(QueueBackgroundWorkItem)安排一个可以在后台运行的任务,独立于   任何要求。这与普通的ThreadPool工作项不同   ASP.NET会自动跟踪已注册的工作项数   通过这个API当前正在运行,而ASP.NET运行时将会运行   尝试延迟AppDomain关闭,直到这些工作项完成   执行。

1 个答案:

答案 0 :(得分:11)

一个简单的解决方案是轮询监控API,直到作业完成为止:

    public static Task Enqueue(Expression<Action> methodCall)
    {
        string jobId = BackgroundJob.Enqueue(methodCall);
        Task checkJobState = Task.Factory.StartNew(() =>
        {
            while (true)
            {
                IMonitoringApi monitoringApi = JobStorage.Current.GetMonitoringApi();
                JobDetailsDto jobDetails = monitoringApi.JobDetails(jobId);
                string currentState = jobDetails.History[0].StateName;
                if (currentState != "Enqueued" && currentState != "Processing")
                {
                    break;
                }
                Thread.Sleep(100); // adjust to a coarse enough value for your scenario
            }
        });
        return checkJobState;
    }

注意:当然,在Web托管的场景中,您无法继续执行任务(task.ContinueWith())以在作业完成后执行更多操作,因为AppDomain可能是关闭 - 出于同样的原因,你可能首先想要使用Hangfire。