从任务中调用函数
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' return 类型,而我只想 task.Result 到 return 我的列表>。有什么办法可以解决这个问题吗?
在您的 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 的评论。他说等待任务开始并同步完成是没有意义的。你应该认真看看awaiting it.
顺便说一句,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
}
我正在编写一个自定义报告工具,允许用户创建一些非常广泛的查询。我想为此添加一个超时,这样如果用户创建的内容最终会 运行 很长,整个系统就不会停止。我想到了这个:
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' return 类型,而我只想 task.Result 到 return 我的列表>。有什么办法可以解决这个问题吗?
在您的 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 的评论。他说等待任务开始并同步完成是没有意义的。你应该认真看看awaiting it.
顺便说一句,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
}