共享资源和异步 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.PerWebRequest
。 BaseTaskRunner
的容器使用 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
上使用 await
从 TaskManager.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.")
};
}
现在它将像您期望的那样异步工作。 await
在 async
状态机中设置了一个继续标记,指示它 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
。
我有一个简单的 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.PerWebRequest
。 BaseTaskRunner
的容器使用 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
上使用 await
从 TaskManager.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.")
};
}
现在它将像您期望的那样异步工作。 await
在 async
状态机中设置了一个继续标记,指示它 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
。