哪种TPL最适合这种情况.NET API?任务/线程



我有一个方法,可以在观看视频时增加视频的视图。如果发布者的视频浏览量达到200次,我会给他发一封祝贺电子邮件,但是如果电子邮件发送延迟,我不希望用户等待该延迟来观看视频。我需要使视频加载不依赖于电子邮件发送。我使用了Task。运行和任务。工厂StartNew及其内部Parallel我没有任务/线程的经验,所以实现它的最佳方式是什么。

public async Task<ServiceResponse<ViewedVideoDto>> AddVideoView(int candidateVideoId)
{
var video = await _dbcontext.CandidateFiles.Include(x=>x.Candidate).ThenInclude(x=>x.User).FirstOrDefaultAsync(x => x.Id == candidateVideoId);
if (video is null)
{
return new ServiceResponse<ViewedVideoDto>().NotFound(nameof(Resource.VideoEmpty), Resource.VideoEmpty);
}
video.VideoViews += 1;
_dbcontext.Update(video);
await _dbcontext.SaveChangesAsync();
if (video.VideoViews == 200)
{
//Method 1 
await Task.Run(() =>
_candidateEmailService.Achieved200VideoViews(video.Candidate.User.Email,
video.Candidate.User.FullName));
//Method 2
Task.Factory.StartNew(() => Parallel.Invoke(() =>
{
_candidateEmailService.Achieved200VideoViews(video.Candidate.User.Email,
video.Candidate.User.FullName);
}));

}
return new ServiceResponse<ViewedVideoDto>()
{
Data = new ViewedVideoDto()
{
VideoId = video.Id
}
};

}
public async Task Achieved200VideoViews(string receiverEmail, string fullName)
{
var candidateTemplate = EmailTemplate.Return(EmailTemplateConst.CanEmailTempFolder, EmailTemplateConst.CanAchieved200VideoViews,fullName);

var innoEmail = new InnoEmail()
{
Email = receiverEmail,
Subject = "Touchpoint after 200 views",
HtmlMessage = candidateTemplate 
};
await _emailService.SendAsync(innoEmail);
public async Task<bool> SendAsync(Email email)
{
try
{
var apiKey = _configuration.GetSection("SENDGRID_API_KEY").GetSection("SecretKey").Value;
var Email = _configuration.GetSection("SENDGRID_API_KEY").GetSection("Email").Value;
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage()
{
From = new EmailAddress(Email, "test"),
Subject = email.Subject,
PlainTextContent = null,
HtmlContent = email.HtmlMessage
};
msg.AddTo(new EmailAddress(email.Email));
var response = await client.SendEmailAsync(msg).ConfigureAwait(false);
response.StatusCode.ToString();
return true;
}
catch (System.Exception e)
{
return false;
}
}

方法1(await Task.Run(() =>(将无法实现声明的目标,因为您正在等待基本上会暂停AddVideoView执行直到Achieved200VideoViews完成的结果。如果你想要即发即弃行为-取消等待。

基于Achieved200VideoViews实现,基本上它只是发送邮件。我想说的是,哪一种更好并不重要,因为即发即弃方法都不够好:它们缺乏可观察性(例如,电子邮件发送可能因多种原因失败,或者在此过程中应用程序将重新启动(。

我建议并采用类似于事务发件箱模式的方法-向video表或单独的表添加一个标志,该表将存储关于发送200个视图的通知的信息(类似于video.ToSend200ViewNotification(,并在AddVideoView中更新该信息,然后在一些后台作业处理程序中处理该表,例如使用托管服务或Hangfire或Quartz(或者添加标志Is200ViewNotificationSend并处理视图>=200和Is200ViewNotificationSend设置为false的所有视频(。

如果您同意即发即弃方法对同步操作的限制,则Task.Run应优先(无await(。正如文档中所写,TaskFactory.StartNew更适合于更复杂的场景(而且它不具有任务意识,在"普通"情况下无法正确处理返回Task的方法(:

从开始。NET Framework 4.5,Task.Run方法是启动计算绑定任务的推荐方法。仅当需要对长时间运行的、绑定计算的任务进行细粒度控制时,才使用StartNew方法。这包括您想要控制以下内容的场景:

  • Task创建选项。默认情况下,使用Task.Run方法创建的任务是使用TaskCreationOptions.DenyChildAttach选项创建的。要覆盖此行为或提供其他TaskCreationOptions选项,请调用StartNew重载
  • 参数传递。Task.Run方法的重载不允许将参数传递给任务委托。StartNew方法的重载确实如此
  • 任务调度程序。Task.Run方法的重载使用默认的任务调度程序。要控制任务调度程序,请使用调度程序参数调用StartNew过载。有关详细信息,请参阅TaskScheduler

但是由于您的Achieved200VideoViews方法已经返回Task,并且假设它是";真正异步";那么只要在调用上跳过await就可以实现即发即弃功能(我想说这是优选的,因为它应该消耗更少的资源(:

_ = _candidateEmailService.Achieved200VideoViews(video.Candidate.User.Email,
video.Candidate.User.FullName);

最新更新