使操作异步

时间:2012-01-19 15:33:30

标签: c# .net multithreading asynchronous

我有一个实现某些服务的大型应用程序,它使用的接口是

IResult Execute(IRequest)

它全部包含在IHttpHandler中。其中一些服务使用我想要替换的通用基础架构组件。问题是,我的组件需要很长时间才能运行,并且由于所有这些都托管在IIS服务器中,因此很快就会耗尽其工作线程。

我可以将服务包装在IHttpAsyncHandler中,但我仍然需要一种方法来异步使用我的组件,否则我将只是持有不同的工作线程。

我当然可以在它自己的线程中执行我的服务,但它非常昂贵。 使用System.Threading.Tasks.Task或Delegate.BeginInvoke,只会占用另一个线程或工作线程,具体取决于实现。

理想情况下,我希望如果可以的话,当我调用长时间运行的操作时,获取线程的堆栈和执行上下文,将它们放在一边,开始做我的工作(主要是IO和异步),释放线程即时使用到线程池\ OS,并在完成时将我保存的上下文放在一边并继续执行我的结果。

这在一些函数式语言中是非常有用的,或者在使用continuation时,如何在C#中实现?


经过一些研究,我认为我需要做的是一种延续。我需要冻结当前的执行线程堆栈,将线程释放回池中,并在我将要使用的异步操作的回调中重新启动冻结的堆栈。


Justin Pihony的请求我正在添加一些代码来更好地描述我的问题。

这是我的Http Handler(当然简化):

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 个答案:

答案 0 :(得分:7)

为什么不使用任务库?在这种情况下,您不需要对线程池进行微观管理。如果任务真的是异步并等待,那么该线程将被其他东西使用,直到异步方法获得它正在等待的值?

尝试使用Tasks及其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}
);

如果你真的看过这个用法,你会注意到线程池非常有效地管理它。