2009-03-23 9 views
46

Mam operację asynchroniczną, która z różnych powodów musi zostać wywołana przy użyciu wywołania HTTP do strony internetowej ASP.NET. Kiedy moja strona jest żądana, powinna rozpocząć tę operację i natychmiast zwrócić potwierdzenie klientowi.Uruchamianie operacji asynchronicznej uruchamianej przez żądanie strony internetowej ASP.NET

Ta metoda jest również wyświetlana za pośrednictwem usługi WWW WCF i działa idealnie.

Na mojej pierwszej próbie, wyjątek został rzucony, mówiąc mi:

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.

więc oczywiście dodałem parametr Async="true" z dyrektywą @Page. Teraz nie otrzymuję błędu, ale strona jest blokowana, dopóki nie zakończy się operacja asynchroniczna.

Jak uzyskać dostęp do prawdziwej strony z zaporą i zapomnieniem?

Edytuj: Kod w celu uzyskania dalszych informacji. To trochę bardziej skomplikowane, ale starałem się stworzyć ogólny pomysł.

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"); 
    } 
} 

Klasa 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

Trudno odpowiedzieć, nie widząc kodu, który wywołuje tę operację asynchroniczną. – Bryan

+0

OK, zaktualizuję pytanie, podając więcej informacji. Dzięki. – Damovisa

+0

Na platformie Azure można używać zadań sieci Web. Zobacz http://curah.microsoft.com/52143/using-the-webjobs-feature-of-windows-azure-web-sites wątków w tle na ASP.NET są problematyczne. – RickAndMSFT

Odpowiedz

28

Jeśli nie dbam o powrocie nic użytkownikowi, można po prostu odpalić albo oddzielnego wątku, albo na szybkie i zabrudzony podejścia, stosowania delegata i powołać się na niego w sposób asynchroniczny. Jeśli nie chcesz powiadamiać użytkownika, gdy kończy się zadanie asynchroniczne, możesz zignorować wywołanie zwrotne. Spróbuj umieścić punkt przerwania na końcu metody SomeVeryLongAction(), a zobaczysz, że to zakończy uruchomiony po stronie została już serwowane:

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

Dzięki, BeginInvoke było kluczem - nie jestem pewien, dlaczego nie próbowałem tego! – Damovisa

+2

Niebezpieczeństwo! Zignorowanie wywołania zwrotnego spowoduje, że zasoby nie zostaną oczyszczone. To jeden z powodów, dla których poleciłem inne podejście. Jeffrey Richter mówi o tym w swoim CLR za pośrednictwem książki C# (może nie mieć tytułu dokładnie dobrze). –

+3

To EndInvoke, które musi zostać wywołane w pewnym momencie, aby zapewnić czyszczenie. Bardzo prawdziwe. Dobra dyskusja: 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, tutaj jest problem: atrybut asynchroniczny jest dla przypadek, w którym strona będzie wywoływać długo działające zadanie, które blokuje wątek, a następnie strona potrzebuje danych wyjściowych z tego zadania, aby zwrócić informacje do użytkownika. Jeśli na przykład Twoja strona musi zadzwonić do usługi internetowej, poczekaj na jej odpowiedź, a następnie użyj danych z odpowiedzi, aby wyrenderować swoją stronę.

Powodem, dla którego chcesz użyć atrybutu Async, jest uniknięcie blokowania wątku. Jest to ważne, ponieważ aplikacje ASP.NET używają puli wątków do obsługi żądań, a dostępna jest tylko stosunkowo niewielka liczba wątków. Jeśli każde połączenie wiąże wątek podczas oczekiwania na wywołanie usługi sieciowej, wkrótce trafisz na tyle równoczesnych użytkowników, że użytkownicy będą musieli poczekać na zakończenie tych wywołań usług internetowych. Atrybut Async pozwala wątkowi wrócić do puli wątków i służyć innym użytkownikom, którzy odwiedzają witrynę jednocześnie, zamiast zmuszać go do pozostawania bezczynnie podczas oczekiwania na zwrot wezwania serwisu WWW.

Wynik jest następujący: atrybut Async jest przeznaczony dla przypadku, w którym nie można wyświetlić strony, dopóki nie zakończy się asynchroniczne zadanie, i dlatego nie powoduje natychmiastowej renderowania strony.

Musisz uruchomić własny wątek i uczynić go wątkiem demona. Nie pamiętam dokładnej składni tego, ale możesz łatwo znaleźć to w dokumencie, przeszukując dokument BCL dla "daemona". Oznacza to, że wątek uniemożliwi zamknięcie aplikacji podczas jej działania, co jest ważne, ponieważ ASP.NET i IIS zastrzegają sobie prawo do "recyklingu procesu", gdy uznają to za konieczne, a jeśli tak się stanie, gdy wątek działa, twoje zadanie zostanie zatrzymane. Ustanowienie demona wątku temu zapobiegnie (z wyjątkiem niektórych możliwych rzadkich przypadków skrajnych ... dowiesz się więcej, kiedy znajdziesz dokumentację na ten temat).

Ten wątek demona służy do rozpoczęcia tych zadań. Po powierzeniu wątku demona, że ​​wykonasz zadanie, możesz natychmiast wyrenderować swoją stronę ... aby renderowanie strony stało się natychmiast.

Jeszcze lepiej niż wątek demona w procesie ASP.NET, byłoby zaimplementowanie usługi systemu Windows do wykonania tego zadania. Poproś, aby aplikacja ASP.NET zakomunikowała zadanie, które ma zostać wykonane w usłudze. Nie potrzebujesz wątku demona i nie musisz się martwić o proces recyklingu ASP.NET. W jaki sposób możesz nakazać usłudze wykonanie tego zadania? Być może poprzez WCF, a może poprzez wstawienie rekordu do tabeli bazy danych, którą usługa ankietuje. Lub na wiele innych sposobów.

EDYCJA: Oto kolejny pomysł, którego użyłem wcześniej w tym samym celu. Zapisz informacje o swoim zadaniu w kolejce MSMQ. Czy inny proces (może nawet na innej maszynie) wyciągnąć z tej kolejki i wykonać czasochłonne zadanie. Zadanie wstawiania do kolejki jest zoptymalizowane tak, aby zwracało się tak szybko, jak to możliwe, więc wątek nie będzie blokował się, gdy dane, które umieścisz w kolejce, zostaną wysłane przez przewód lub coś w tym stylu. Jest to jeden z najszybszych sposobów zwrócenia uwagi na fakt, że zadanie musi zostać wykonane bez czekania na wykonanie tego zadania.

+0

Świetna odpowiedź - dzięki za to. Właściwie używam MSMQ w linii. Jedyne, co naprawdę musiałem zrobić, to uruchomić kolejny wątek, aby wykonać moją pracę zgodnie z sugestią. Jak wspomniałeś, parametr Async w dyrektywie @Page nie był mi pomocny. – Damovisa

+0

Dobrze, cieszę się, że pomogło. BTW, pamiętaj, aby zobaczyć mój komentarz na temat drugiej odpowiedzi ... ignorowanie strony EndInvoke rzeczy spowoduje wyciek zasobów!Jedynym niezawodnym sposobem na sprawdzenie EndInvoke jest posiadanie wątku demona. Wróciłeś do tego lub MSMQ/Service. –

+0

Dzięki za to - ponownie wykorzystałem kod, który zrobił coś podobnego i po prostu nie zrobił nic na EndInvoke. – Damovisa

1

Jeśli pojawi się ten błąd podczas asynchronicznego wywoływania usługi WWW, należy upewnić się, że dodano atrybut Async = "true" zgodnie z instrukcją wyjątku ?

góry < Page Language = 'VB' Async = 'true' AutoEventWireup = 'false' CodeFile = 'mynewpage.aspx.vb' Dziedziczy 'mynewpage' =%>

+0

Nie zapomnij oznaczyć jako ans jeśli pomógł – Amrik

+3

Odpowiedź na to pytanie została zaakceptowana 3 lata temu. W pytaniu oryginalnym wspomniano również o Async = true. Witamy w SO. –

+0

@Nigdy, jeśli przyjrzysz się uważnie, odpowiedziałem na to w 2012 roku. Czy dostałeś szansę na głosowanie w dół, który skopiował moją odpowiedź w 2013 roku i zaakceptował 14 razy? – Amrik

2

Można obejść to ograniczenie dość łatwo i nawet nie ustawiając Async na true.

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

jeśli używasz webforms ustawić Ansync = „true” na stronie .aspx gdzie robisz żądania.
<%@ Page Language="C#" Async="true" ... %>

Powiązane problemy