2009-03-23 31 views
46

Tôi có một thao tác không đồng bộ vì nhiều lý do cần được kích hoạt bằng cách sử dụng một cuộc gọi HTTP đến một trang web ASP.NET. Khi trang của tôi được yêu cầu, nó sẽ bắt đầu hoạt động này và ngay lập tức trả lại một xác nhận cho khách hàng.Chạy một hoạt động không đồng bộ được kích hoạt bởi một yêu cầu trang web ASP.NET

Phương pháp này cũng được hiển thị qua dịch vụ web WCF và nó hoạt động hoàn hảo.

On nỗ lực đầu tiên của tôi, một ngoại lệ được ném ra, nói với tôi:

Asynchronous operations are not allowed in this context. 
Page starting an asynchronous operation has to have the Async 
attribute set to true and an asynchronous operation can only be 
started on a page prior to PreRenderComplete event.

Vì vậy, tất nhiên tôi đã thêm tham số Async="true" để chỉ thị @Page. Bây giờ, tôi không gặp lỗi, nhưng trang đang chặn cho đến khi thao tác Không đồng bộ hoàn tất.

Làm thế nào để tôi có được một trang thực sự hoạt động?

Chỉnh sửa: Một số mã để biết thêm thông tin. Đó là một chút phức tạp hơn này, nhưng tôi đã cố gắng để có được ý tưởng chung trong đó.

public partial class SendMessagePage : System.Web.UI.Page 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     string message = Request.QueryString["Message"]; 
     string clientId = Request.QueryString["ClientId"]; 

     AsyncMessageSender sender = new AsyncMessageSender(clientId, message); 
     sender.Start(); 

     Response.Write("Success"); 
    } 
} 

Lớp AsyncMessageSender:

public class AsyncMessageSender 
{ 
    private BackgroundWorker backgroundWorker; 
    private string client; 
    private string msg; 

    public AsyncMessageSender(string clientId, string message) 
    { 
     this.client = clientId; 
     this.msg = message; 

     // setup background thread to listen 
     backgroundThread = new BackgroundWorker(); 
     backgroundThread.WorkerSupportsCancellation = true; 
     backgroundThread.DoWork += new DoWorkEventHandler(backgroundThread_DoWork); 
    } 

    public void Start() 
    { 
     backgroundThread.RunWorkerAsync(); 
    } 

    ... 
    // after that it's pretty predictable 
} 
+0

Khó trả lời mà không thấy mã gọi hoạt động không đồng bộ này. – Bryan

+0

Ok, tôi sẽ cập nhật câu hỏi với nhiều thông tin hơn. Cảm ơn. – Damovisa

+0

Trên Azure, bạn có thể sử dụng Công việc Web. Xem http://curah.microsoft.com/52143/using-the-webjobs-feature-of-windows-azure-web-sites Các chủ đề nền trên ASP.NET có vấn đề. – RickAndMSFT

Trả lời

28

Nếu bạn không quan tâm đến việc trở lại bất cứ điều gì cho người sử dụng, bạn chỉ có thể cháy lên hoặc là một chủ đề riêng biệt, hoặc cho một cách tiếp cận nhanh chóng và dơ bẩn, sử dụng một đại biểu và gọi nó một cách đồng thời. Nếu bạn không quan tâm đến việc thông báo cho người dùng khi tác vụ không đồng bộ kết thúc, bạn có thể bỏ qua cuộc gọi lại. Hãy thử đặt một breakpoint ở phần cuối của phương pháp SomeVeryLongAction(), và bạn sẽ thấy rằng nó kết thúc hoạt động sau khi trang đã được phục vụ lên:

private delegate void DoStuff(); //delegate for the action 

protected void Page_Load(object sender, EventArgs e) 
{ 

} 

protected void Button1_Click(object sender, EventArgs e) 
{ 
    //create the delegate 
    DoStuff myAction = new DoStuff(SomeVeryLongAction); 
    //invoke it asynchrnously, control passes to next statement 
    myAction.BeginInvoke(null, null); 
    Button1.Text = DateTime.Now.ToString(); 
} 


private void SomeVeryLongAction() 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     //simulation of some VERY long job 
     System.Threading.Thread.Sleep(100); 
    } 
} 
+0

Cảm ơn, BeginInvoke là chìa khóa - Tôi không chắc tại sao tôi không thử cái đó! – Damovisa

+2

Mặc dù nguy hiểm! Bỏ qua việc gọi lại sẽ dẫn đến các tài nguyên không được dọn dẹp. Đó là một lý do tôi đề nghị một cách tiếp cận khác. Jeffrey Richter nói về điều này trong CLR của mình thông qua cuốn sách C# (tôi có thể không có tiêu đề chính xác). –

+3

Đó là EndInvoke phải được gọi vào một thời điểm nào đó để đảm bảo dọn dẹp. Rất đúng. Thảo luận tốt: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d88d3f1e-f4aa-40c1-b1b6-e79e801f3909/ http://msdn.microsoft.com/en- us/magazine/cc164036 (printer) .aspx –

25

OK, đây là vấn đề: các thuộc tính async là dành cho trường hợp trang của bạn sẽ gọi một số nhiệm vụ chạy dài cũng chặn luồng, và sau đó trang của bạn cần đầu ra từ nhiệm vụ đó để trả lại thông tin cho người dùng. Ví dụ: nếu trang của bạn cần gọi dịch vụ web, hãy chờ phản hồi của nó và sau đó sử dụng dữ liệu từ phản hồi để hiển thị trang của bạn.

Lý do bạn sử dụng thuộc tính Không đồng bộ là để tránh chặn luồng. Điều này quan trọng bởi vì các ứng dụng ASP.NET sử dụng một hồ bơi thread để phục vụ các yêu cầu, và chỉ có một số lượng tương đối nhỏ các luồng có sẵn. Và nếu mỗi cuộc gọi liên kết chuỗi trong khi chờ cuộc gọi dịch vụ web, thì bạn sẽ sớm đạt được đủ người dùng đồng thời mà người dùng sẽ phải chờ cho đến khi các cuộc gọi dịch vụ web này hoàn tất. Thuộc tính Async cho phép luồng trở về nhóm luồng và phục vụ các khách truy cập đồng thời khác vào trang web của bạn, thay vì buộc nó phải ngồi yên trong khi đợi cuộc gọi dịch vụ web trở lại.

Bản cập nhật cho bạn là: thuộc tính Async được thiết kế cho trường hợp bạn không thể hiển thị trang cho đến khi tác vụ không đồng bộ hoàn tất và đó là lý do tại sao nó không hiển thị trang ngay lập tức.

Bạn cần khởi chạy chuỗi của riêng mình và đặt chuỗi chủ đề đó thành daemon. Tôi không nhớ cú pháp chính xác cho điều đó, nhưng bạn có thể dễ dàng tìm thấy nó trong tài liệu bằng cách tìm kiếm tài liệu BCL cho "daemon". Điều này có nghĩa là thread sẽ giữ ứng dụng của bạn không hoạt động khi nó còn sống, điều quan trọng vì ASP.NET và IIS bảo lưu quyền "tái chế quy trình của bạn" khi họ cho là cần thiết và nếu điều đó xảy ra trong khi chuỗi của bạn đang hoạt động, nhiệm vụ của bạn sẽ bị dừng lại. Làm cho daemon thread sẽ ngăn chặn điều này (ngoại trừ một số trường hợp hiếm gặp có thể xảy ra ... bạn sẽ tìm hiểu thêm khi bạn tìm thấy tài liệu về điều này).

Chủ đề daemon đó là nơi bạn sẽ khởi động các tác vụ này. Và sau khi bạn đã nói với chuỗi daemon để thực hiện tác vụ, bạn có thể ngay lập tức hiển thị trang của mình ... do đó việc hiển thị trang sẽ diễn ra ngay lập tức.

Thậm chí tốt hơn là một chuỗi daemon trong tiến trình ASP.NET của bạn, tuy nhiên, sẽ thực hiện một Dịch vụ Windows để thực hiện tác vụ. Có ứng dụng ASP.NET của bạn giao tiếp nhiệm vụ được thực hiện với Dịch vụ. Không cần cho một chủ đề daemon và không cần phải lo lắng về quá trình ASP.NET của bạn đang được tái chế. Làm thế nào để bạn yêu cầu Dịch vụ thực hiện nhiệm vụ? Có lẽ thông qua WCF, hoặc có lẽ bằng cách chèn một bản ghi vào một bảng cơ sở dữ liệu mà các cuộc thăm dò dịch vụ. Hoặc một số cách khác.

EDIT: Đây là một ý tưởng khác mà tôi đã sử dụng trước đây cho mục đích này. Viết thông tin về nhiệm vụ của bạn vào hàng đợi MSMQ. Có một quá trình khác (thậm chí có thể trên một máy khác) kéo từ hàng đợi đó và thực hiện nhiệm vụ tốn thời gian. Công việc chèn vào Hàng đợi được tối ưu hóa để trả về nhanh nhất có thể, do đó, chuỗi của bạn sẽ không chặn trong khi dữ liệu bạn đưa vào Hàng đợi được gửi qua dây hoặc bất kỳ thứ gì tương tự. Đây là một trong những cách nhanh nhất để lưu ý thực tế là một nhiệm vụ cần phải được thực hiện mà không cần chờ đợi nhiệm vụ đó thực thi.

+0

Câu trả lời hay - cảm ơn vì điều đó. Tôi đang thực sự sử dụng MSMQ xuống dòng một chút. Tất cả những gì tôi thực sự cần làm là khởi chạy một chủ đề khác để thực hiện công việc của tôi như bạn đã đề xuất. Như bạn đã đề cập, tham số Async trong chỉ thị @Page sẽ không giúp tôi. – Damovisa

+0

Tốt, vui vì nó đã giúp. BTW, hãy chắc chắn để xem bình luận của tôi về câu trả lời khác ... bỏ qua phía EndInvoke của sự vật sẽ gây ra một rò rỉ tài nguyên!Và cách đáng tin cậy duy nhất để chắc chắn rằng bạn có thể xử lý EndInvoke là có một chuỗi daemon. Vì vậy, bạn quay lại đó hoặc MSMQ/Dịch vụ. –

+0

Cảm ơn vì điều đó - Tôi thực sự đã sử dụng lại một số mã đã làm một điều tương tự và không làm bất cứ điều gì trên EndInvoke. – Damovisa

1

Nếu bạn gặp lỗi này khi gọi dịch vụ web không đồng bộ, hãy đảm bảo thêm thuộc tính Async = 'true' theo hướng dẫn bởi thông báo ngoại lệ ?

đầu trang < Page Language = 'VB' Async = 'true' AutoEventWireup = 'false' CodeFile = 'mynewpage.aspx.vb' Inherits 'mynewpage' =%>

+0

đừng quên đánh dấu là ans nếu đã giúp – Amrik

+3

Câu trả lời cho câu hỏi này đã được chấp nhận 3 năm trước. Ngoài ra Async = true đã được đề cập trong câu hỏi ban đầu. Chào mừng bạn đến với SO. –

+0

@nathanchere nếu bạn xem xét kỹ tôi đã trả lời câu hỏi này vào năm 2012. bạn có cơ hội bỏ phiếu bầu đã sao chép câu trả lời của tôi vào năm 2013 và được chấp nhận 14 lần không? – Amrik

2

Bạn có thể làm việc xung quanh giới hạn này khá dễ dàng và thậm chí không đặt Async thành true.

public void Start() 
{ 
    new Task(() => 
    { 
     backgroundThread.RunWorkerAsync(); 
    }).Start(); 
} 
21

Nếu bạn đang chạy biểu mẫu web được đặt Ansync = "true" trong trang .aspx nơi bạn đang thực hiện yêu cầu.
<%@ Page Language="C#" Async="true" ... %>

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