C# 在过期时异步调用操作

C# 在过期时异步调用操作,c#,.net,multithreading,asynchronous,delegates,C#,.net,Multithreading,Asynchronous,Delegates,我有一个典型的异步消息调度问题。本质上,我需要异步分派消息,然后在分派完成时捕获消息响应。问题是,我似乎不知道如何使任何一个请求周期自行过期和短路 以下是我目前使用的模式示例: 为调用定义的委托 private delegate IResponse MessageDispatchDelegate(IRequest request); private void DispatchMessageCallback(IAsyncResult ar) { //Get result from EndIn

我有一个典型的异步消息调度问题。本质上,我需要异步分派消息,然后在分派完成时捕获消息响应。问题是,我似乎不知道如何使任何一个请求周期自行过期和短路

以下是我目前使用的模式示例:

为调用定义的委托

private delegate IResponse MessageDispatchDelegate(IRequest request);
private void DispatchMessageCallback(IAsyncResult ar)
{
  //Get result from EndInvoke(r) which could be IResponse or a Timeout Exception
}
通过回调发送消息

var dispatcher = new MessageDispatchDelegate(DispatchMessage);
dispatcher.BeginInvoke(requestMessage, DispatchMessageCallback, null);
发送消息

private IResponse DispatchMessage(IRequest request)
{
  //Dispatch the message and throw exception if it times out
}
将调度结果作为响应或异常获取

private delegate IResponse MessageDispatchDelegate(IRequest request);
private void DispatchMessageCallback(IAsyncResult ar)
{
  //Get result from EndInvoke(r) which could be IResponse or a Timeout Exception
}

我不知道的是如何在DispatchMessage方法中干净地实现超时/短路过程。任何想法都将受到欢迎。

经过一番努力,我终于能够为我原来的问题找到解决方案。首先,让我说我得到了很多很好的回复,我测试了所有的回复(用结果对每个回复进行评论)。主要问题是,所有建议的解决方案要么导致死锁(导致100%超时场景),要么使原本异步的进程同步。我不喜欢回答自己的问题(有史以来第一次),但在这种情况下,我采纳了StackOverflow常见问题解答的建议,因为我真正学到了自己的经验教训,并希望与社区分享

        var dispatcher = new MessageDispatchDelegate(DispatchMessage);

        var asyncResult = dispatcher.BeginInvoke(requestMessage, DispatchMessageCallback, null);
        if (!asyncResult.AsyncWaitHandle.WaitOne(1000, false))
        {
             /*Timeout action*/
        }
        else
        {
            response = dispatcher.EndInvoke(asyncResult);
        }
最后,我将建议的解决方案与delagates调用结合到备用AppDomain中。它的代码多一点,成本也高一点,但这避免了死锁,并允许完全异步调用,这正是我所需要的。这是一些片段

首先我需要一些东西来调用另一个AppDomain中的委托

/// <summary>
/// Invokes actions in alternate AppDomains
/// </summary>
public static class DomainInvoker
{
    /// <summary>
    /// Invokes the supplied delegate in a new AppDomain and then unloads when it is complete
    /// </summary>
    public static T ExecuteInNewDomain<T>(Delegate delegateToInvoke, params object[] args)
    {
        AppDomain invocationDomain = AppDomain.CreateDomain("DomainInvoker_" + delegateToInvoke.GetHashCode());

        T returnValue = default(T);
        try
        {
            var context = new InvocationContext(delegateToInvoke, args);
            invocationDomain.DoCallBack(new CrossAppDomainDelegate(context.Invoke));

            returnValue = (T)invocationDomain.GetData("InvocationResult_" + invocationDomain.FriendlyName);
        }
        finally
        {
            AppDomain.Unload(invocationDomain);
        }
        return returnValue;
    }

    [Serializable]
    internal sealed class InvocationContext
    {
        private Delegate _delegateToInvoke;
        private object[] _arguments;

        public InvocationContext(Delegate delegateToInvoke, object[] args)
        {
            _delegateToInvoke = delegateToInvoke;
            _arguments = args;
        }

        public void Invoke()
        {
            if (_delegateToInvoke != null)
                AppDomain.CurrentDomain.SetData("InvocationResult_" + AppDomain.CurrentDomain.FriendlyName,
                    _delegateToInvoke.DynamicInvoke(_arguments));
        }
    }
}
//
///调用备用AppDomains中的操作
/// 
公共静态类DomainInvoker
{
/// 
///在新AppDomain中调用提供的委托,然后在完成后卸载
/// 
public static T ExecuteInNewDomain(委托delegateToInvoke,参数对象[]args)
{
AppDomain invocationDomain=AppDomain.CreateDomain(“DomainInvoker_389;”+delegateToInvoke.GetHashCode());
T返回值=默认值(T);
尝试
{
var context=新的调用上下文(delegateToInvoke,args);
DoCallBack(新的CrossAppDomainDelegate(context.Invoke));
returnValue=(T)invocationDomain.GetData(“InvocationResult_u2;”+invocationDomain.FriendlyName);
}
最后
{
卸载(调用域);
}
返回值;
}
[可序列化]
内部密封类调用上下文
{
私人代表(u delegateToInvoke);;
私有对象[]_参数;
公共调用上下文(委托delegateToInvoke,对象[]args)
{
_delegateToInvoke=delegateToInvoke;
_参数=args;
}
公共无效调用()
{
如果(_delegateToInvoke!=null)
AppDomain.CurrentDomain.SetData(“调用结果”+AppDomain.CurrentDomain.FriendlyName,
_delegateToInvoke.DynamicInvoke(_参数));
}
}
}
第二我需要一些东西来协调所需参数的收集并收集/解析结果。这还将定义超时和工作进程,这些进程将在备用AppDomain中异步调用

/// <summary>
/// Invokes actions in alternate AppDomains
/// </summary>
public static class DomainInvoker
{
    /// <summary>
    /// Invokes the supplied delegate in a new AppDomain and then unloads when it is complete
    /// </summary>
    public static T ExecuteInNewDomain<T>(Delegate delegateToInvoke, params object[] args)
    {
        AppDomain invocationDomain = AppDomain.CreateDomain("DomainInvoker_" + delegateToInvoke.GetHashCode());

        T returnValue = default(T);
        try
        {
            var context = new InvocationContext(delegateToInvoke, args);
            invocationDomain.DoCallBack(new CrossAppDomainDelegate(context.Invoke));

            returnValue = (T)invocationDomain.GetData("InvocationResult_" + invocationDomain.FriendlyName);
        }
        finally
        {
            AppDomain.Unload(invocationDomain);
        }
        return returnValue;
    }

    [Serializable]
    internal sealed class InvocationContext
    {
        private Delegate _delegateToInvoke;
        private object[] _arguments;

        public InvocationContext(Delegate delegateToInvoke, object[] args)
        {
            _delegateToInvoke = delegateToInvoke;
            _arguments = args;
        }

        public void Invoke()
        {
            if (_delegateToInvoke != null)
                AppDomain.CurrentDomain.SetData("InvocationResult_" + AppDomain.CurrentDomain.FriendlyName,
                    _delegateToInvoke.DynamicInvoke(_arguments));
        }
    }
}
注意:在我的测试中,我扩展了dispatch worker方法,以随机花费一定的时间来观察在超时和非超时情况下一切都按预期工作

public委托i响应DispatchMessageWithTimeoutDelegate(IRequest请求,int timeout=MessageDispatcher.DefaultTimeoutMs);
[可序列化]
公共密封类消息调度器
{
public const int DefaultTimeoutMs=500;
/// 
///在多个线程上调用Public方法以发送超时请求
/// 
公共IResponse SendRequest(IRequest请求,int超时)
{
var dispatcher=newdispatchmessagewithtimeoutdelegate(SendRequestWithTimeout);
返回DomainInvoker.ExecuteInNewDomain(调度程序、请求、超时);
}
/// 
///进程调用的Worker方法
/// 
专用IResponse SendRequestWithTimeout(IRequest请求,int超时)
{
i响应=空;
var dispatcher=新DispatchMessageDelegate(DispatchMessage);
//请求调度
var asyncResult=dispatcher.BeginInvoke(请求,null,null);
//等待调度完成,如果时间过长,则短路
如果(!asyncResult.AsyncWaitHandle.WaitOne(超时,false))
{
/*超时动作*/
响应=空;
}
其他的
{
/*调用的调用在超时时间内结束*/
response=dispatcher.EndInvoke(异步结果);
}
返回响应;
}
/// 
///Worker方法在监视超时时执行实际调度工作
/// 
专用IResponse DispatchMessage(IRequest请求)
{
/*在这里做真正的调度工作*/
返回新的响应();
}
}
第三我需要一些东西来代替异步触发调度的实际内容

注意:这只是为了演示我需要的异步行为。实际上,上面的第一个第二个项演示了在备用线程上隔离超时行为。这只是演示如何使用上述资源

公共委托IResponse DispatchMessageDelegate(IRequest请求);
班级计划
{
接收到静态int_响应;
静态void Main()
{
常数int max=500;
对于(int i=0;i