共享资源和异步 Web API 调用



我有一个简单的Web API方法,看起来像这样:

public async Task<HttpResponseMessage> RunTask(TaskType taskType)
{
    var taskId = await TaskManager.CreateTask(taskType);
    TaskManager.Run(taskId);
    return new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.OK,
        Content =
            new StringContent($"Task {taskType.GetDescription()} was started.")
    };
}

TaskManager.Run是这样描述的:

public async Task Run(int id)

我希望它会在TaskManager.Run(taskId)后立即返回"任务已启动"消息 但是请求继续同步运行。

但如果将呼叫TaskManager.Run(taskId)替换为:

 Task.Run(() => Thread.Sleep(TimeSpan.FromSeconds(100)));

然后它异步运行。

所以我相信这与TaskManager和主线程共享的资源有关。共享资源是否可以锁定执行?

我正在使用温莎城堡。一个WindsorContainer容器在 Web API 项目中声明。TaskManager在其中使用BaseTaskRunner类。BaseTaskRunner中宣布了另一个WindsorContainer。Web API 的容器对所有组件使用 LifeStyle.PerWebRequestBaseTaskRunner的容器使用LifeStyle.Singleton(不确定它是否正确的生活方式(。例如,调用是否可以被 DdContext 或两个容器中声明的其他类锁定?

上级:我不想等待 TaskManager.Run 完成。但是发生的情况是,返回语句仍然等待 TaskManager.Run 完成(即使 TaskManager.Run 上没有 await 语句(。换句话说,我如何调用任务管理器.Run并不重要:

TaskManager.Run(taskId);

await TaskManager.Run(taskId);

在这两种情况下,它都会等待 TaskManager.Run 完成。

这是任务管理器的代码:

 public class TaskManager : ITaskManager
    {
        public IRepository<BackgroundTask> TaskRepository { get; set; }
        public async Task<int> CreateTask(TaskType type, byte[] data = null, object config = null)
        {
            var task = new BackgroundTask
            {
                Type = type,
                Status = BackgroundTaskStatus.New,
                Config = config?.SerializeToXml(),
                Created = DateTime.Now,
                Data = data
            };
            TaskRepository.Add(task);
            TaskRepository.SaveChanges();
            return task.Id;
        }
        public async Task Run(int id, bool removeOnComplete = true)
        {
            var task = TaskRepository.GetById(id);
            Run(task, removeOnComplete);
        }
        public async Task Run(TaskType type, bool removeOnComplete = true)
        {
            var tasksToRun = TaskRepository.Get(t => t.Type == type);
            tasksToRun.ForEachAsync(t => Run(t, removeOnComplete));
        }
        public async Task Run(BackgroundTask task, bool removeOnComplete = true)
        {
            switch (task.Type)
            {
                case TaskType.SpreadsheetImport:
                    new SpreadsheetImportTaskRunner().Run(task);
                    break;                    
            }                  
        }
}

和其他一些类:

public class SpreadsheetImportTaskRunner : BaseTaskRunner
    {
        public IForecastSpreadsheetManager SpreadsheetManager { get; set; }
        protected override void Execute()
        {
            SpreadsheetManager.ImportActuals(Task.Data);
        }
        protected override void Initialize()
        {
            base.Initialize();
            SpreadsheetManager = _container.Resolve<IForecastSpreadsheetManager>();
        }
    }

BaseTaskRunner:

public class BaseTaskRunner
    {
        public IRepository<BackgroundTask> TaskRepository { get; set; }
        protected IWindsorContainer _container = new WindsorContainer();
        protected BackgroundTask Task { get; set; }
        public async Task Run(BackgroundTask task)
        {
            Initialize();
            Task = task;
            try
            {               
                Execute();               
            }
            catch (Exception ex)
            {
                SetError(ex.ToString());
            }
        }
        protected virtual void Execute()
        {
        }
        protected virtual void Initialize()
        {
            _container.Install(new TaskRunnerComponentsInstaller());
            TaskRepository = _container.Resolve<IRepository<BackgroundTask>>();
        }   
    }

我仍然相信这与温莎容器和在几个不同线程中解析的公共类有关。

问题是您没有在从 TaskManager.Run 函数调用返回的Task上使用await。请考虑以下事项:

public async Task<HttpResponseMessage> RunTask(TaskType taskType)
{
    var taskId = await TaskManager.CreateTask(taskType);
    await TaskManager.Run(taskId);
    return new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.OK,
        Content =
            new StringContent($"Task {taskType.GetDescription()} was started.")
    };
}

现在,它将像您期望的那样异步工作。awaitasync状态机中设置一个继续标记,指示它在完成TaskManager.Run中定义的异步操作后返回到方法的这一部分。

更新

您缺少很多await语句,有时不需要将方法标记为async。似乎有一些误解,因为它与这些关键字有关。这是您的TaskManager类应该是什么样子的。

public class TaskManager : ITaskManager
{
    public IRepository<BackgroundTask> TaskRepository { get; set; }
    public async Task<int> CreateTask(TaskType type, 
                                      byte[] data = null, 
                                      object config = null)
    {
        var task = new BackgroundTask
        {
            Type = type,
            Status = BackgroundTaskStatus.New,
            Config = config?.SerializeToXml(),
            Created = DateTime.Now,
            Data = data
        };
        TaskRepository.Add(task);
        TaskRepository.SaveChanges();
        return task.Id;
    }
    public ask Run(int id, bool removeOnComplete = true)
    {
        var task = TaskRepository.GetById(id);
        return Run(task, removeOnComplete);
    }
    public Task Run(TaskType type, bool removeOnComplete = true)
    {
        var tasksToRun = TaskRepository.Get(t => t.Type == type);
        return tasksToRun.ForEachAsync(t => Run(t, removeOnComplete));
    }
    public Task Run(BackgroundTask task, bool removeOnComplete = true)
    {
        switch (task.Type)
        {
            case TaskType.SpreadsheetImport:
                return new SpreadsheetImportTaskRunner().Run(task);
                break;                    
            }                  
        }
    }
}

理想情况下,如果该方法标记为返回类型的Task,并且该方法不需要在其执行中展开任何任务,则只需返回其实现的Task功能。例如,请注意我的TaskManager类与您的类有很大不同 - 我只是将方法标记为需要实际await async。这两个关键字应该结合,如果一个方法使用async应该有一个await。但仅当方法需要展开并使用异步操作时,才使用 await

最新更新