未等待任务中的等待任务

时间:2018-04-03 17:56:32

标签: c# async-await task dotnet-httpclient

我正在处理一个从数据库队列中选择消息的小型控制台应用程序 并将消息转发给rest api(ASP.NET Web Api)。

通常,应用程序执行以下步骤:

  1. 获取待处理邮件的数量
  2. 加载上次待处理的消息
  3. 将消息发布到其余的api
  4. 从数据库中删除邮件
  5. 使程序更灵活,并能够处理每条消息 在单独的数据库转换中,步骤2 - 3将作为任务执行。

    这意味着如果数据库中有四条消息,我们将有四个任务 应该几乎并行运行并处理消息。

    这就是代码的样子:

    数据库消息

    public class DatabaseMessage 
    {
        public string Message { get; set; }
    }
    

    UnitOfWork(接口IUnitOfWork)

    public class UnitOfWork 
    {
        // ... extermely simplified
        public int GetNumberOfPendingMessages() { ... }
        public DatabaseMessage GetNextPendingMessage() { ... }
        public void DeleteMessage(DatabaseMessage message) { ... }
    }
    

    HttpService(接口IHttpService)

    public class HttpService
    {
        private readonly HttpClient _httpClient;
    
        public HttpService() 
        {
            _httpClient = new HttpClient();
            /* Some initalization stuff for the HttpClient object */
        }
    
        public async Task<HttpResponse> PostMessage(DatabaseMessage message) 
        {
            var content = /* Create content object */
            return await _httpClient.PostAsync(..., content);
        }
    }
    

    MessageProcessingService(接口IMessageProcessingService)

    public class MessageProcessingService 
    {
        private readonly IHttpService _httpService;
        private readonly Semaphore _databaseProcessingSemaphore;
    
        public MessageProcessingService(IHttpService httpService) 
        {
            _httpService = httpService;
        }
    
        public async Task ProcessDatabaseMessages() 
        {
            var unitOfWork = new UnitOfWork();
            var numberOfPendingMessages = unitOfWork.GetNumberOfPendingMessages();
    
            var messageProcessingTasks = new List<Task>();
    
            for(int t = 0; t < numberOfPendingMessages; t++) 
            {
                messageProcessingTasks.Add(new Task(() => {
                    ProcessMessageAsTask();
                }));
            }
    
            var continuationHandler = Task.WhenAll(messageProcessingTasks);
    
            messageProcessingTasks.ForEach(e => e.Start());
    
            await continuationHandler;
        }
    
        private void ProcessMessageAsTask() 
        {
            // Single unit of work for each tasks
            var unitOfWork = new UnitOfWork();
    
            try{
                // Starting a database transaction
                unitOfWork.StartTransaction();
                _databaseProcessingSemaphore.OnWait();
                var message = unitOfWork.GetNextPendingMessage();
                _databaseProcessingSemaphore.Release();
    
                if(message != null) 
                {
                    var response = _httpService.PostMessage(message).Result;
    
                    if(response == HttpStatus.OK) 
                    {
                        unitOfWork.DeleteMessage(message);
                        unitOfWork.Commit();
                    }
                    else 
                    {
                        unitOfWork.Rollback();
                    }
    
                }
                else 
                {
                    unitOfWork.Commit();
                }
            }
            catch(Exception ex) 
            {
                unitOfWork.Rollback();
                // Further error handling...
            }
        }
    }
    

    为了更好地理解,HttpClient对象由Unity创建和管理并被注入 进入MessageProcessingService对象。 HttpClient在容器中保持为单例。

    我现在面临调用方法_httpService.PostMessage()的问题。例如,如果有五个 消息队列中的消息,调用失败五次,异常告诉我任务已被取消。

    我的问题是,现在.NET HttpClient的PostAsync调用有什么问题?该问题是由.Result选项引起的还是会引起的 为每个消息处理任务创建一个新的HttpClient实例会更好吗? 或者,具有任务的架构和休息api调用的处理是否存在普遍问题?

    更新2018-04-04 - 08:09

    我现在使方法ProcessMessageAsTask异步,我现在正在等待HttpService的调用。 但现在我根本没有任何例外。在资源监视器和调试过程中,我可以看到所有任务都可以调用HttpClientreturn await _httpClient.PostAsync(..., content);) 但是没有例外,也不会发布消息。但我没有任何例外。该程序将在HttpClient的调用后立即关闭。所有进一步的陈述都没有得到处理。

    的变化:

    public async Task ProcessDatabaseMessages() 
    {
        var unitOfWork = new UnitOfWork();
        var numberOfPendingMessages = unitOfWork.GetNumberOfPendingMessages();
    
        var messageProcessingTasks = new List<Task>();
    
        for(int t = 0; t < numberOfPendingMessages; t++) 
        {
            messageProcessingTasks.Add(new Task(async () => {
                await ProcessMessageAsTask();
            }));
        }
    
        var continuationHandler = Task.WhenAll(messageProcessingTasks);
    
        messageProcessingTasks.ForEach(e => e.Start());
    
        await continuationHandler;
    }
    
    private async Task ProcessMessageAsTask() 
    {
        // Single unit of work for each tasks
        var unitOfWork = new UnitOfWork();
    
        try{
            // Starting a database transaction
            unitOfWork.StartTransaction();
            _databaseProcessingSemaphore.OnWait();
            var message = unitOfWork.GetNextPendingMessage();
            _databaseProcessingSemaphore.Release();
    
            if(message != null) 
            {
                var response = await _httpService.PostMessage(message);
    
                if(response == HttpStatus.OK) 
                {
                    unitOfWork.DeleteMessage(message);
                    unitOfWork.Commit();
                }
                else 
                {
                    unitOfWork.Rollback();
                }
    
            }
            else 
            {
                unitOfWork.Commit();
            }
        }
        catch(Exception ex) 
        {
            unitOfWork.Rollback();
            // Further error handling...
        }
    }
    

0 个答案:

没有答案