共享资源和异步 Web API 调用

Shared resource and async Web API call

我有一个简单的 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) 之后立即发送 return "Task was started" 消息,但请求继续同步 运行。

但是,如果将调用 TaskManager.Run(taskId) 替换为:

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

然后它 运行 是异步的。

所以我认为这与 TaskManager 和主线程共享的资源有关。共享资源可以锁定执行吗?

我正在使用温莎城堡。 Web API 项目中声明了一个 WindsorContainer 容器。 TaskManager 在其中使用了 BaseTaskRunner class。在 BaseTaskRunner 中又声明了一个 WindsorContainer。 Web API 的容器对所有组件使用 LifeStyle.PerWebRequestBaseTaskRunner 的容器使用 LifeStyle.Singleton(不确定它是否正确的 LifeStyle)。是否可以通过 DdContext 或在两个容器中声明的其他 classes 锁定调用?

更新: 我不想等待 TaskManager.Run 完成。但是发生的是 return 语句仍在等待 TaskManager.Run 完成(即使没有 TaskManager.Run 上的 await 语句)。 换句话说,我如何调用 TaskManager.Run:

并不重要
TaskManager.Run(taskId);

await TaskManager.Run(taskId);

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

这里是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 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;                    
            }                  
        }
}

和其他一些 classes:

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

我仍然认为这与 WindsorContainer 和常见的 classes 有关,它们在几个不同的线程中得到解决。

问题是您没有在 Task 上使用 awaitTaskManager.Run 函数的调用中 returned。考虑以下:

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 状态机中设置了一个继续标记,指示它 return 在 TaskManager.Run 中定义的异步操作完成后进入方法的这一部分。

更新

您遗漏了很多 await 语句,有时您不需要将方法标记为 async。似乎对这些关键字存在一些误解。这是您的 TaskManager class 的样子。

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 的 return 类型,并且该方法不需要在其执行过程中展开任何任务,它可以简单地 return Task 的实现功能。例如,请注意我的 TaskManager class 与您的有多么不同——我只是将方法标记为 async 而实际上需要 await。这两个关键字应该结合起来,如果一个方法使用 async 应该有一个 await。但仅当方法需要展开并使用异步操作时才使用 await