共享资源和异步Web API调用

时间:2016-02-12 17:27:21

标签: c# multithreading async-await asp.net-web-api2 castle-windsor

我有一个简单的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)

我期待它回归&#34;任务已经开始&#34;在TaskManager.Run(taskId)之后立即发出消息但是请求继续同步运行。

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

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

然后它以异步方式运行。

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

我正在使用Castle Windsor。在Web API项目中声明了一个WindsorContainer容器。 TaskManager在其中使用BaseTaskRunner类。在WindsorContainer中声明了另外一个BaseTaskRunner。 Web API的容器对所有组件使用LifeStyle.PerWebRequestBaseTaskRunner的容器使用LifeStyle.Singleton(不确定它是否是正确的LifeStyle)。是否可以通过DdContext或在两个容器中声明的其他类来锁定调用?

UPD: 我不想等待TaskManager.Run完成。但是,返回语句仍然等待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;                    
            }                  
        }
}

以及其他一些课程:

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以及在几个不同线程中解决的常见类有关。

1 个答案:

答案 0 :(得分:1)

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

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类与您的类别有多么不同 - 我只是将方法标记为需要实际async的{​​{1}}。这两个关键字应该结婚,如果方法使用await,则应该有async。但是,如果方法需要展开并使用异步操作,则只使用await