我有一个循环过程。在循环的每次迭代期间,它调用外部Web服务,然后将对象添加到EntityFramework存储库。对外部服务的调用包含在静态方法中。通常,循环只有一次或两次迭代,但UI目前最多可以有4次。 (每次迭代代表一份保险报价)。
这似乎可以从重构为异步过程中受益。如何设置它以便每次迭代都在一个单独的线程中进行,并且提交会一直等到所有线程都完成?
public class ProcessRequest
{
private IUnitOfWork = unitOfWork;
public ProcessRequest(IUnitOfWork uow)
{
unitOfWork = uow;
}
public void Execute(MyRequestParams p)
{
foreach (Quote q in p.Quotes)
{
q.Premium = QuoteService.GetQuote(q);
unitOfWork.GetRepository<Quote>().Add(q);
}
unitOfWork.Commit();
}
}
public static class QuoteService
{
public static decimal GetQuote(Quote quote)
{
//I've simplified proprietary code to single line that calls an external service
return ExternalWebService.GetQuote(quote.Deductible);
}
}
答案 0 :(得分:0)
你问两个不同的东西:一个是如何并行执行循环 ,其中每个迭代(可能)在一个单独的线程上发生;这与将整个循环作为异步进程执行完全不同,这意味着启动它的线程不会等待它完成。我假设你的意思是第一个,即你希望在循环中并行化迭代但仍然阻塞直到所有这些迭代完成。
在不知道运行它的上下文的情况下,一种直接的方法是使用Parallel Extensions,特别是Parallel Foreach:
public void Execute(MyRequestParams p)
{
Parallel.ForEach(p.Quotes, q => {
q.Premium = QuoteService.GetQuote(q);
unitOfWork.GetRepository<Quote>().Add(q);
});
unitOfWork.Commit();
}
或者类似的东西:
public void Execute(MyRequestParams p)
{
Parallel.ForEach(p.Quotes, q => {
q.Premium = QuoteService.GetQuote(q);
});
unitOfWork.GetRepository<Quote>().AddAll(p.Quotes);
unitOfWork.Commit();
}
这在很大程度上取决于您正在处理的线程安全性。
答案 1 :(得分:0)
如果您的大部分工作都是I / O,我不确定我是否会开始新线程,因为您浪费大部分时间闲置等待服务/ DB回复。
我尝试使用异步方法:
public async Task Execute(MyRequestParams p)
{
foreach (var quote in p.Quotes)
{
//Of course, you'll need an async endpoint.
var q.Premium = await QuoteService.GetQuoteAsync(q);
}
unitOfWork.GetRepository<Quote>().AddAll(p.Quotes);
await unitOfWork.SaveChangesAsync();
}
使用这种方法,您可以节省启动新线程的开销,并让它们在大多数时间处于空闲状态。 希望这是有道理的,当然您必须能够访问Web服务的异步端点,并使用Entity Framework v6。