Выполнение асинхронной операции, вызванной запросом веб-страницы ASP.NET - программирование
Подтвердить что ты не робот

Выполнение асинхронной операции, вызванной запросом веб-страницы ASP.NET

У меня есть асинхронная операция, которая по разным причинам должна запускаться с использованием HTTP-вызова на веб-страницу ASP.NET. Когда моя страница запрашивается, она должна начать эту операцию и немедленно вернуть подтверждение клиенту.

Этот метод также отображается через веб-службу WCF, и он отлично работает.

С моей первой попытки исключение было брошено, сказав мне:

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.

Поэтому, конечно, я добавил параметр Async="true" в директиву @Page. Теперь я не получаю сообщение об ошибке, но страница блокируется до завершения асинхронной операции.

Как я могу получить истинную страницу с огнем и забытью?

Изменить: Некоторый код для получения дополнительной информации. Это немного сложнее, чем это, но я попытался получить там общую идею.

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

Класс 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 pretty predictable
}
4b9b3361

Ответ 1

Если вы не хотите возвращать что-либо пользователю, вы можете просто запустить либо отдельный поток, либо быстрый и грязный подход, использовать делегат и вызывать его асинхронно. Если вам не нужно уведомлять пользователя о завершении задачи async, вы можете игнорировать обратный вызов. Попытайтесь положить точку останова в конце метода SomeVeryLongAction(), и вы увидите, что она заканчивается после того, как страница уже отправлена:

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

Ответ 2

ОК, здесь проблема: атрибут Async предназначен для случая, когда ваша страница будет вызывать некоторую долгосрочную задачу, которая также блокирует поток, а затем ваша страница нуждается в выходе из этой задачи, чтобы вернуть информацию в Пользователь. Например, если ваша страница необходима для вызова веб-службы, дождитесь ее ответа, а затем используйте данные из ответа, чтобы отобразить вашу страницу.

Причиной использования атрибута Async является предотвращение блокировки потока. Это важно, потому что приложения ASP.NET используют пул потоков для обслуживания запросов, и доступно лишь относительно небольшое количество потоков. И если каждый вызов связывает поток при ожидании вызова веб-службы, то вскоре вы столкнетесь с достаточным количеством одновременных пользователей, которым придется ждать, пока вызовы этих веб-сервисов не будут завершены. Атрибут Async позволяет потоку возвращаться в пул потоков и обслуживать других одновременных посетителей на вашем веб-сайте, а не заставлять его сидеть и ничего не делать, ожидая возврата вызова веб-службы.

Результат для вас: атрибут Async предназначен для случая, когда вы не можете отобразить страницу до завершения асинхронной задачи и почему она не отображает страницу сразу.

Вам нужно запустить собственный поток и сделать его потоком демона. Я не помню точного синтаксиса для этого, но вы можете легко найти его в документе, выполнив поиск документа BCL для "daemon". Это означает, что поток будет закрывать ваше приложение, пока он жив, что важно, потому что ASP.NET и IIS оставляют за собой право "переработать ваш процесс", когда они сочтут это необходимым, и если это произойдет во время работы вашего потока, ваша задача будет остановлена. Создание демона потока предотвратит это (за исключением некоторых возможных случаев редких краев... вы узнаете больше, когда найдете документацию по этому вопросу).

Этот поток демона - это то, где вы начнете выполнять эти задачи. И после того, как вы сказали потоку демона выполнить задачу, вы можете немедленно отобразить свою страницу... так что рендеринг страницы произойдет немедленно.

Тем не менее, даже лучше, чем поток демона в вашем процессе ASP.NET, будет реализовывать службу Windows для выполнения этой задачи. Попросите приложение ASP.NET передать задачу, которая будет выполняться в Службе. Нет необходимости в потоке демона, и вам не нужно беспокоиться о том, что ваш ASP.NET-процесс перерабатывается. Как вы сообщаете Службе о выполнении этой задачи? Возможно, через WCF, или, возможно, путем вставки записи в таблицу базы данных, которую опроса Службы. Или несколько других способов.

EDIT: Вот еще одна идея, которую я использовал раньше для этой же цели. Напишите информацию о своей задаче в очередь MSMQ. Попросите другой процесс (возможно, даже на другой машине) вытащить из этой очереди и выполнить трудоемкую задачу. Задача вставки в очередь оптимизирована для возврата как можно быстрее, поэтому ваш поток не будет блокироваться, а данные, которые вы помещаете в очередь, отправляются по проводке или что-то в этом роде. Это один из самых быстрых способов обратить внимание на то, что нужно выполнить задачу, не дожидаясь выполнения этой задачи.

Ответ 3

Если вы используете webforms, установите Ansync = "true" на странице .aspx, где вы делаете запрос.
<%@ Page Language="C#" Async="true" ... %>

Ответ 4

Вы можете легко обойти это ограничение и даже не установить Async в true.

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

Ответ 5

Если вы получаете эту ошибку при асинхронном вызове веб-службы, убедитесь, что добавили атрибут Async = 'true', как указано в сообщение об исключении?

вверху страницы < Язык страницы = 'VB' Async = 'true' AutoEventWireup = 'false' CodeFile = 'mynewpage.aspx.vb' Наследует = 'mynewpage'% >