使操作异步

AK_*_*AK_ 0 .net c# multithreading asynchronous

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

IResult Execute(IRequest)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

这在一些函数式语言中是非常可能的,或者在使用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
        }
    }
Run Code Online (Sandbox Code Playgroud)

我无法改变"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);
    }
}
Run Code Online (Sandbox Code Playgroud)

好吧.Net这样做是不可能的.实际上它是,但需要大量的工作,并且可能不会产生任何性能优势,而不是创建一个完整的线程.这样的方法仅在本质上更具功能性的语言(或平台)中有益,并且具有与Win-API正在使用的内存和线程模型相当不同的内存和线程模型.

虽然如果最初以异步方式编写代码,或者依赖于TPL,他很可能很容易实现这一点......

我正在向贾斯汀颁发赏金,如果不是因为讨论的答案.

Jus*_*ony 7

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

尝试使用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}
);
Run Code Online (Sandbox Code Playgroud)

如果你真的在观察用法,你会发现线程池非常有效地管理它.

  • @kenny,这是一个很好的澄清,async并不意味着线程实际上被浪费了.线程池是智能的,不会浪费时间等待异步.所以,async!= thread并且异步和并行之间存在差异.在考虑你的线程时请记住这一点.这可能是我们误解的来源 (3认同)
  • 我刚编辑了我的代码来解决这个问题.您将注意到TaskScheduler.FromCurrentSynchronizationContext(),它应该处理异步返回时需要同步的任何代码.这样,它将启动一个适当的线程,但在异步操作完成后跳回到调用线程.如果我正确理解你的话.如果依赖于异步数据的同步代码必须等待异步数据,那么这仍然是我看到的最佳解决方案......否则这从一开始就是一个同步方法. (2认同)