我有一个方法,可以在观看视频时增加观看的点击量. 如果发布者的视频点击量达到200次,我会给他发一封祝贺邮箱, 但如果邮箱发送延迟,我不希望用户等待延迟才观看视频. 我需要使视频加载不依赖于邮箱发送. 我使用过Task.Run和Task.Factory.StartNew和Inside It Parallell 我没有使用任务/线程的经验,所以哪种方法是实现它的最佳方式.

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;
            }
        }

推荐答案

Method 1(await Task.Run(() =>)不会达到宣布的目标,因为您正在等待结果,这基本上会暂停AddVideoView的执行,直到Achieved200VideoViews完成.如果你想要"一发即忘"的行为--go 掉等待.

基于Achieved200VideoViews个实施,基本上它只是发送邮件-我想说,哪一个更好并不重要,因为两种即发即忘的方法都不够好,因为它们缺乏可观察性(例如,邮箱发送可能因为多种原因而失败,或者应用程序将在过程中重新启动).

我的建议和方法类似于transactional outbox pattern-添加一个标志到video表或单独的表,它将存储有关200个浏览量发送的通知的信息(类似于video.ToSend200ViewNotification),并在AddVideoView中更新它,然后在一些后台作业(job)处理程序中处理该表,例如使用hosted services或Hangout或Quartz(或添加标志Is200ViewNotificationSend并处理浏览量&>=200和Is200ViewNotificationSend设置为False的所有视频).

如果您可以接受"即发即忘"的同步操作方法的局限性,Task.Run应该是更好的(没有await).Task.Factory.StartNew,如在docs中编写的TaskFactory.StartNew更适合于更复杂的场景(而且它不是任务感知的,在"普通"情况下将不能正确处理返回Task的方法):

从.NET Framework4.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);

Csharp相关问答推荐

Blazor:计算值或保留为默认值

使用其可能实现的基类和接口的属性的方法

为什么我不能更改尚未设置的模拟对象属性的值?

有没有一种方法可以防止在编译时在MicrosoftC或非单线程上下文中调用方法?

. NET在上一个操作完成之前,在此上下文实例上启动了第二个操作

此反射有什么问题.是否发送值转换委托?

Polly使用泛型重试和重试包装函数

Azure Redis缓存与Entra ID身份验证

只有第一个LINQ.Count()语句有效

我想在文本框悬停时在其底部显示一条线

Blazor在FluentButton onClick事件上设置参数

当我没有此令牌时,为什么语法报告EOF错误?

Polly重试URL复制值

当我将`ConcurentDictionary`转换为`IDictionary`时,出现了奇怪的并发行为

Celler ArgumentExpression是否期望在所有情况下都捕获允许空值的运算符?

Blazor Server/.NET 8/在初始加载时调用异步代码是否冻结屏幕,直到第一次异步调用完成?

为什么我的自定义Json.net转换器不工作?

Maui:更改代码中的绑定字符串不会更新UI,除非重新生成字符串(MVVM)

为什么Visual Studio 2022建议IDE0251将我的方法设置为只读?

初始化具有EntityFrameworkCore和不同架构的数据库时引发System.MissingMethodExcept