Сделать операцию асинхронной - PullRequest
0 голосов
/ 19 января 2012

У меня есть большое приложение, которое реализует некоторые сервисы, интерфейс которого он использует:

IResult Execute(IRequest)

Все это заключено в IHttpHandler.Некоторые из этих сервисов используют общий компонент инфраструктуры, который я хочу заменить.Проблема в том, что моему компоненту требуется много времени для запуска, и, поскольку все это размещено на сервере IIS, оно очень быстро исчерпает свои рабочие потоки.

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

Я, конечно, могу просто выполнить свой сервис внутри своего собственного потока, но это очень дорого.использование System.Threading.Tasks.Task или Delegate.BeginInvoke просто задержит другой поток или рабочий поток, в зависимости от реализации.

В идеале я хотел бы, если бы я мог, когда вызывается моя длительная операция, взять стек потока и контекст выполнения, сохранить их в стороне, начать выполнять свою работу (которая в основном является IO и асинхронной сама по себе), освободите поток, который я использую, в пул потоков \ OS, и когда закончите, отведите контекст, который я сохранил, и продолжите выполнение с моим результатом.

Это очень возможно в некоторых функциональных языках или при использовании продолжения, как можноэто должно быть достигнуто в C #?


После некоторого исследования, я думаю, что то, что мне нужно сделать, является своего рода продолжением.Мне нужно заморозить мой текущий стек потоков выполнения, освободить поток обратно в пул и перезапустить замороженный стек при обратном вызове из асинхронной операции, которую я буду использовать.


По запросу Джастина ПихониЯ добавляю некоторый код, чтобы лучше описать мою проблему.

Это мой обработчик Http (упрощение, конечно):

class Handler: IHttpAsyncHandler 
{
        private readonly Action<HttpContext> _processDelegate = ProcessRequest;

        public void ProcessRequest(HttpContext context)
        {
           IBuissnessService blThing = IOC.Get(context.Something);

           // usually doesnt take too long...
           thing.DoWork(context);
        }


        public bool IsReusable
        {
            get
            {
                return true;
            }
        }

        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            return _processDelegate.BeginInvoke(context, cb, extraData);
        }

        public void EndProcessRequest(IAsyncResult result)
        {
            _processDelegate.EndInvoke(result);
        }
}

//this is the buissness object

    class BLThing :IBuissnessService 
    {
        public void DoWork(HttpContext)
        { 
        //.... a Lot of Stuff

        // in some point one of the objects this is using does:
        IDoWork someObject = IOC.GetSomthing();
        var result = someObject.DoWork();

        // uses the result some more
        // and eventually returns.
        }
    }

    class SomeObject : IDoWork 
    {
        public Result DoWork()
        {
         // does some very long http call to another server
        }
    }

Я не могу изменить «BLThing», но я полностьюуправление "SomeObject" также в конечном итоге мне нужно поддерживать тот же интерфейс, который ожидает "BLThing".

Я хочу добавить к "SomeObject" два асинхронных метода (BeginDoWork, EndDoWork) и написать:

class SomeObjectWrapper : IDoWork 
{
    SomeObject _worker ;
    public Result DoWork()
    {
         worker = new SomeObject();
         ThreadState state = CurrentThread.CaptureState();
         worker.BeginDoWork(Callback,state)
         CurrentThread.PurgeStateAndReturnToPool();
    }

    void Callback(IAsyncResult result)
    {
       var processingResult = worker.EndDoWork(result);
       ThreadState state  =(ThreadState) result.AsyncState;
       state.ReturnTopCall(processingResult);
       CurrentThread.RestoreThreadState(state);
    }
}

Ну в .Net сделать это невозможно.На самом деле это так, но потребует много работы и, вероятно, не даст никакого преимущества в производительности по сравнению с созданием полноценного потока.Подобный подход будет полезен только на языке (или платформе), который по своей природе гораздо более функционален и имеет модель памяти и потоков, отличающуюся от той, которую использует Win-API.

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

Я присуждаю награду Джастину, если не за ответ, то за обсуждение.

1 Ответ

7 голосов
/ 20 января 2012

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

Попробуйте использовать Задачи и их функции ContinueWith

Task t = Task.Factory.StartNew(code);
Task taskForSynchronous = t.ContinueWith(
    (previousTask)=>{synchronous code after t is done},
    TaskScheduler.FromCurrentSynchronizationContext()
);
newTask.ContinueWith(
    (previousTask)=>{more asynchronous code}
);

Если вы действительно наблюдаете за использованием, вы заметите, что пул потоков управляет этим довольно эффективно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...