繁体   English   中英

从任务中调用Func

[英]Calling a Func From a Task

我正在编写一个自定义报告工具,允许用户创建一些非常广泛的查询。 我想为此添加一个超时,这样如果用户创建的东西最终会运行很长时间,整个系统就不会停止。 我想出了这个:

public List<List<SimpleDisplayField>> FindReport(int reportId)
    {
        var report = GetReportById(reportId);

        var tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
        int timeOut = 20000; // 2 seconds

        if (report.BidType == "LOB")
        {
            var task = Task.Factory.StartNew(() => FindLOBReport(report), token);
            if (!task.Wait(timeOut, token))
            {
                throw new Exception("Report ran for more than 10 seconds.. run again or add more filters.");  
            }
            return task.Result;
        }
        else
        {
            var task = Task.Factory.StartNew(() => FindFWOReport(report), token);
            if (!task.Wait(timeOut, token))
            {
                throw new Exception("Report ran for more than 10 seconds.. run again or add more filters.");
            }
            return task.Result;
        }
    }

这很好,但我想将它重构为这样的东西,使用Func,所以我可以将FindLOBReport或FindFWOReport作为参数传递:

public List<List<SimpleDisplayField>> FindReport(int reportId)
    {
        var report = GetReportById(reportId);

        if (report.BidType == "LOB")
        {
            return RunReport(FindLOBReport(report));
        }
        else
        {
            return RunReport(FindFWOReport(report));
        }
    }

    private List<List<SimpleDisplayField>> RunReport(Func<CustomReport, List<List<SimpleDisplayField>>> method)
    {
        var tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
        int timeOut = 20000; // 2 seconds

        var task = Task.Factory.StartNew(() => method, token);
        if (!task.Wait(timeOut, token))
        {
            throw new Exception("Report ran for more than 10 seconds.. run again or add more filters.");
        }

        return task.Result;
    }

但是,task.Result是一个'Func'返回类型,而我只想让task.Result返回我的List>。 有什么方法可以解决这个问题吗?

RunReport方法中,您实际上并没有调用method Func。 您正在重新调整method委托。 这就是Task.Result被推断为Func<>

var task = Task.Factory.StartNew(() => method, token);

上面的代码等于

var task = Task.Factory.StartNew(() =>
                                 {
                                    return method;
                                 }, token);

要执行它,您需要使用方法调用语法来调用它。

var task = Task.Factory.StartNew(() => method(report), token);

为此,您需要将报告作为参数。

private List<List<SimpleDisplayField>> RunReport(Func<CustomReport, List<List<SimpleDisplayField>>> method,CustomReport report)
{
    var tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    int timeOut = 20000; // 2 seconds

    var task = Task.Factory.StartNew(() => method(report), token);
    if (!task.Wait(timeOut, token))
    {
        throw new Exception("Report ran for more than 10 seconds.. run again or add more filters.");
    }

    return task.Result;
}

然后你可以称之为

public List<List<SimpleDisplayField>> FindReport(int reportId)
{
    var report = GetReportById(reportId);
    return (report.BidType == "LOB")
           ? RunReport(FindLOBReport, report)
           : RunReport(FindFWOReport, report);
}

另外值得注意的是,您的Task未被取消。 它将继续运行。 需要注意的是一件重要的事情。 如果您的FindLOBReport方法本质上调用数据库,如果这需要花费时间 - 您最好使用SqlCommand.Timeout属性。 这将取消基础操作。

尊重@ YuvalItzchakov的评论。 他说在等待启动任务并等待同步完成任务时没有意义。 你应该认真看待等待它

Btw 20000毫秒不是2秒。 这是20秒。

感谢大家的反馈。 我现在的解决方案如下:

public List<List<SimpleDisplayField>> FindReport(int reportId)
    {
        var report = GetReportById(reportId);
        return (report.BidType == "LOB")
               ? RunReportAsync(FindLOBReport, report).Result
               : RunReportAsync(FindFWOReport, report).Result;
    }

    private async Task<List<List<SimpleDisplayField>>> RunReportAsync(Func<CustomReport, List<List<SimpleDisplayField>>> method, CustomReport report)
    {
        var task = await Task.Factory.StartNew(() => method.DynamicInvoke(report));
        return (List<List<SimpleDisplayField>>)task;
    }

在FindLOB / FWOReport中我使用它来超时查询:

 using (TRACSEntities db = new TRACSEntities())
            {
                db.Database.CommandTimeout = 60;
                var query = // and so on
            }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM