Веб-сервис, выполняющий асинхронный вызов - PullRequest
1 голос
/ 31 мая 2010

У меня есть метод веб-сервиса FetchNumber (), который выбирает число из базы данных и затем возвращает его вызывающей стороне. Но непосредственно перед тем, как он возвращает номер вызывающей стороне, ему необходимо отправить этот номер другой службе, чтобы он создал и запустил BackgroundWorker, задача которого - отправить этот номер другой службе.

public class FetchingNumberService : System.Web.Services.WebService
{
    [WebMethod]
    public int FetchNumber() 
    {
        int value = Database.GetNumber();
        AsyncUpdateNumber async = new AsyncUpdateNumber(value);
        return value;
    }
}

public class AsyncUpdateNumber
{
    public AsyncUpdateNumber(int number)
    {
        sendingNumber = number;

        worker = new BackgroundWorker();
        worker.DoWork += asynchronousCall;
        worker.RunWorkerAsync();
    }

    private void asynchronousCall(object sender, DoWorkEventArgs e)
    {
        // Sending a number to a service (which is Synchronous) here
    }

    private int sendingNumber;
    private BackgroundWorker worker;

}

Я не хочу блокировать веб-службу (FetchNumber ()) при отправке этого номера в другую службу, поскольку это может занять много времени, и вызывающему абоненту не нужно отправлять номер другой службе. Звонящий ожидает, что это вернется как можно скорее.

FetchNumber () делает фоновый работник и запускает его, затем завершает работу (пока работник работает в фоновом потоке). Мне не нужен отчет о прогрессе или возвращаемое значение от фонового работника. Это скорее концепция огня и забвения.

Мой вопрос такой. Поскольку объект веб-службы создается для каждого вызова метода, что произойдет, когда вызванный метод (в данном случае FetchNumber ()) будет завершен, а фоновый работник, который он установил и выполнил, все еще работает?

Что происходит с фоновым потоком? Когда GC собирает объект службы? Это мешает фоновому потоку работать правильно до конца? Есть ли другие побочные эффекты в фоновом потоке?

Спасибо за любой вклад.

Ответы [ 2 ]

0 голосов
/ 01 июня 2010

Переписать один веб-метод

[WebMethod]
public int FetchNumber() 
{
    int value = Database.GetNumber();
    AsyncUpdateNumber async = new AsyncUpdateNumber(value);
    return value;
}

как два с и асинхронный делегат:

public delegate AsyncUpdateNumber GetAsyncUpdateNumber(object state, int value);

[WebMethod]
public IAsyncResult BeginFetchNumber(AsyncCallback cb, object state) 
{
    int value = Database.GetNumber();
    AsyncUpdateNumber async = new AsyncUpdateNumber(value);
    GetAsyncUpdateNumber getAsyncUpdateNumber = new GetAsyncUpdateNumber(async.DoLongRunningThing);

    return getAsyncUpdateNumber.BeginInvoke(state, cb, getAsyncUpdateNumber);
}

[WebMethod]
public int EndFetchNumber(IAsyncResult res) 
{
    GetAsyncUpdateNumber getAsyncUpdateNumber = (GetAsyncUpdateNumber)res.AsyncState;

    return getAsyncUpdateNumber.EndInvoke(res);
}

Они будут отображаться как единый веб-метод с именем FetchNumber(). BeginInvoke() in BeginFetchNumber() будет выполняться асинхронно. Я должен был создать метод с именем DoLongRunningThing, который находится в AsyncUpdater, для выполнения делегата. Переместите задачу, которую вам нужно сделать, асинхронно из конструктора в этот новый метод. Надеюсь, это поможет.

0 голосов
/ 01 июня 2010

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

Циклическая ссылка создается классом AsyncUpdateNumber, который ссылается на BackgroundWorker и регистрирует событие в BackgroundWorker, создавая ссылку на экземпляр класса AsyncUpdateNumber.

Итак, что вы можете сделать ... рассмотрите возможность использования одного из следующих параметров вместо BackgroundWorker:

  1. Использовать тему.
  2. Использовать BeginInvoke.
  3. Используйте ThreadPool.

Образец 1:

var thread = new Thread(new ParameterizedThreadStart((v) => { /* do stuff */ }));
thread.Start(value);

Образец 2:

var func = new Action<int>(v => { /* do stuff */ });
func.BeginInvoke(value, null, null);

Образец 3:

var threadProc = new Action<object>(v => { /* do stuff - note v is of tyoe object */ });
ThreadPool.QueueUserWorkItem(new WaitCallback(threadProc));

Edit:

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

Однако ваш класс является ярким примером циклической ссылки, которая будет восстановлена ​​только после завершения процесса приложения. Это одна из этих более тонких вещей в .NET, которая - даже в управляемой системе - может вызвать утечки памяти ... события создают сильные ссылки .

Хотя в случае одного звонка это не вызывает никаких проблем, если вы нажмете несколько звонков, это действительно может стать проблемой.

...