1

我有一個看起來像這樣一個簡單的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是decalared這樣的:

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(不確定它是否正確LifeStyle)。可以通過DdContext或其他在這兩個容器中聲明的類來鎖定該調用嗎?

UPD: 我不想等待TaskManager.Run完成。但是會發生什麼情況是,return語句仍然等待TaskManager.Run完成(即使沒有在TaskManager.Run上等待語句)。 換句話說,它不會不管我怎麼叫TaskManager.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>>(); 
     } 
    } 

我仍然認爲這是值得做的WindsorContainer以及在幾個不同的線程中解決的常見類。

+0

這很混亂 - 你爲什麼需要等待TaskManager.CreateTask(taskType)而不是TaskManager.Run(taskId);?你試圖等待哪個操作? –

+0

我需要等待TaskManager.CreateTask(taskType),因爲它重新調整了taskId。然後,我將taskId傳遞給Run方法,並希望它在後臺運行,同時將「任務已啓動」消息返回給用戶。 – user1016945

+0

但爲什麼獲取taskId需要異步?你完全理解異步/等待模式(或一般的異步編程)嗎? –

回答

1

問題在於,您在調用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中定義的異步操作後返回到該方法的這一部分。

UPDATE

你缺少大量的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,實際上需要await。這兩個關鍵字應該已結婚,如果方法使用async,則應該有一個await。但如果方法需要展開並使用異步操作,則只能使用await

+0

假設'TaskManager.Run()'返回一個任務,你的答案是正確的。 –

+0

這就是它在問題 –

+0

中的定義是的,沒有注意到它:) –