【问题标题】:Calling a Func From a Task从任务调用函数
【发布时间】:2015-04-28 20:14:30
【问题描述】:

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

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 返回我的列表>。有什么办法可以补救吗?

【问题讨论】:

  • FindLOBReport 是做什么的?它调用数据库吗?顺便说一句,你没有打电话给method
  • 您的函数采用CustomReport - 您希望它接收什么参数? FindLOBReport 的签名是什么?我怀疑您对为委托转换提供方法组和调用方法之间的区别感到有些困惑。
  • @JonSkeet 您的怀疑很可能是正确的。 FindLOBReport 接受一个 CustomReport 并返回一个 2D 列表。
  • @logan_gabriel:是的,听起来您可能想致电RunReport(FindLOBReport) - 但RunReport 也需要接受report。或者你可以调用RunReport(() =&gt; FindLOBReport(report)),把RunReport的参数改成Func&lt;List&lt;List&lt;SimpleDisplayField&gt;&gt;&gt;
  • 您正在执行一个任务并同步等待它。你真的需要这个任务吗?

标签: c# task func


【解决方案1】:

在您的RunReport 方法中,您实际上并没有调用method Func。您正在重新调整 method 代表。这就是Task.Result 被推断为Func&lt;&gt; 的原因。

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 方法本质上调用了 Database 并且这需要时间——您最好使用 SqlCommand.Timeout 属性。这将取消基础操作。

尊重@YuvalItzchakov 的评论。他说等待任务的启动和同步完成是没有意义的。你应该认真看看awaiting it

顺便说一句,20000 毫秒不是 2 秒。这是 20 秒。

【讨论】:

  • 执行后同步等待一个任务?来吧.. ;p
  • @YuvalItzchakov 该方法存在几个问题:\ Stackoverflow 帖子对我来说还不够。您是否注意到方法完成后任务仍在运行。
  • 是的,请在顶部查看我对 OP 的问题。他需要解决几件事。
【解决方案2】:

感谢大家的反馈。我的解决方案现在看起来像这样:

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
            }

【讨论】:

    猜你喜欢
    • 2015-10-19
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多