2013-02-15 33 views
7

Tôi có một REST API MVC4 cố gắng khởi chạy quy trình trong một chuỗi mới. Tôi đang làm việc với framework 4.5 và cố gắng sử dụng đồng bộ và chờ đợi các clausules.Trình khởi chạy quy trình MVA4 WebApi

Mã của tôi trông giống như:

[AcceptVerbs("POST")] 
public HttpResponseMessage Launch(string id) 
{ 
    runProcessAsync(id); // 1               

    return Request.CreateResponse(HttpStatusCode.Accepted); // 2 
} 

protected async void runProcessAsync(string id) 
{ 
    int exitcode = await runProcess(id); // 3 
    string exitcodesz = string.Format("exitcode: {0}", exitcode); // 4 
    // do more stuff with exitcode 
} 

protected Task<int> runProcess(string id) 
{ 
    var tcs = new TaskCompletionSource<int>(); 

    var process = new Process 
    { 
     StartInfo = { FileName = @"C:\a_very_slow_task.bat" }, 
      EnableRaisingEvents = true 
     }; 
     process.Exited += (sender, args) => tcs.SetResult(((Process)sender).ExitCode);    

     process.Start(); 

     return tcs.Task; 
    } 
} 

Lý tưởng nhất, ai đó sẽ thực hiện một cuộc gọi api còn lại (/ nhiệm vụ/slowtask/khởi động) với POST động từ và mong đợi một 202 (chấp nhận) rất nhanh.

Sử dụng trình gỡ lỗi web Fiddler Tôi thực hiện yêu cầu, mã sẽ được đưa vào Khởi chạy (// 1), sau đó chuyển đến phần chờ (// 3), tác vụ chậm được tạo và bắt đầu và Đã chấp nhận trả lại (// 2). Nhưng tại thời điểm này, Fiddler không hiển thị kết quả 202. Xem hình ảnh kèm theo:

http://imageshack.us/photo/my-images/703/fiddler1.png/

Khi nhiệm vụ chậm kết thúc, các mã tiếp tục chụp exit code (// 4) và sau đó là 202 được chụp vào Fiddler.

Điều đó rất lạ, bởi vì tôi đã quay trở lại từ lâu. Những gì tôi đang mất tích? làm thế nào tôi có thể thay đổi mã đó để trả về 202 rất nhanh và quên đi nhiệm vụ.

Lưu ý: Tôi biết cách thực hiện điều này với các tính năng không thuộc khung 4.5, tôi đang cố gắng tìm hiểu cách sử dụng async/await.

+1

Tôi nghĩ rằng điều này là do 'runProcessAsync()' chạy trên bối cảnh đồng bộ hóa của yêu cầu. Nếu bạn không muốn điều đó, bạn có thể sử dụng 'Task.Run (() => runProcessAsync (id));'. Nhưng hãy cẩn thận với điều này, bởi vì IIS có thể tái chế appdomain của bạn trong thời gian đó, do đó, có một cơ hội 'runProcessAsync()' sẽ không hoàn thành. – svick

+0

Tôi đã làm điều đó và hoạt động. Đó là một sự xấu hổ bạn đã không đặt một câu trả lời thích hợp, gây ra bạn giải quyết vấn đề của tôi – Jordi

Trả lời

4

Tôi nghĩ điều này là do runProcessAsync() chạy trên ngữ cảnh đồng bộ hóa của yêu cầu. Nếu bạn không muốn điều đó, bạn có thể sử dụng Task.Run(() => runProcessAsync(id));. Nhưng hãy cẩn thận với điều này, bởi vì IIS có thể tái chế AppDomain của bạn trong thời gian đó, do đó, có một cơ hội runProcessAsync() sẽ không hoàn thành.

1

Cách dễ nhất là chỉ cần thay đổi runProcessAsync để trả lại Task. Hãy nhớ rằng, các phương thức async phải trả lại Task/Task<T> bất cứ khi nào có thể và chỉ trả lại void khi chúng .

Tuy nhiên, tôi phải cảnh báo bạn rằng điều này khá nguy hiểm. ASP.NET là một máy chủ HTTP, vì vậy nếu không có yêu cầu hoạt động, nó sẽ cảm thấy tự do để lấy xuống AppDomain của bạn nếu nó muốn. Điều này có nghĩa là "làm nhiều thứ hơn với exitcode" của bạn sẽ chỉ rơi ra khỏi mặt đất.

Tôi có blog post with a BackgroundTaskManager mà bạn có thể sử dụng để đăng ký Task s với thời gian chạy ASP.NET. Nó sẽ cố gắng trì hoãn tắt máy AppDomain nếu có đăng ký Task s chưa hoàn thành. Tuy nhiên, đó chỉ là một "nỗ lực tốt nhất"; không có sự bảo đảm cho loại điều đó. Bất kỳ mã nào chạy trong ASP.NET không được liên kết với một yêu cầu hoạt động là một tình huống nguy hiểm.

+0

Tôi không hiểu, tại sao sẽ thay đổi kiểu trả về của 'Task' giúp đỡ? Nếu vấn đề là với bối cảnh đồng bộ hóa, thì điều đó sẽ không thay đổi bất cứ điều gì, phải không? – svick

Các vấn đề liên quan