我有一个实现某些服务的大型应用程序,它使用的接口是
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,他可能很容易实现这一点......
我正在向贾斯汀颁发赏金,如果不是因为讨论的答案。
答案 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}
);
如果你真的看过这个用法,你会注意到线程池非常有效地管理它。