C# 在单个扩展方法中正确地开始和结束调用

C# 在单个扩展方法中正确地开始和结束调用,c#,asynchronous,delegates,extension-methods,func,C#,Asynchronous,Delegates,Extension Methods,Func,我想和其他人一起仔细检查一下,这是否是创建一个扩展方法的正确方法,该扩展方法开始一个异步进程,并返回一个函数,当调用该函数时,该函数基本上会等待该进程并获得结果 public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg, Action<IAsyncResult> callback) { IAsyncResult result =

我想和其他人一起仔细检查一下,这是否是创建一个扩展方法的正确方法,该扩展方法开始一个异步进程,并返回一个函数,当调用该函数时,该函数基本上会等待该进程并获得结果

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg, Action<IAsyncResult> callback)
    {
        IAsyncResult result = function.BeginInvoke(arg, new AsyncCallback(callback), function);

        return delegate
        {
            return function.EndInvoke(result);
        };
    }
public static Func HandleInvoke(此Func函数,T参数,动作回调)
{
IAsyncResult result=function.BeginInvoke(arg,新的异步回调(回调),function);
返回代表
{
返回函数.EndInvoke(结果);
};
}
本质上,我希望像这样使用它(伪代码):

Func myFunc=(一些委托).HandleInvoke(arg,callback);
//此时操作开始,但将是非阻塞的
//做其他事情
var result=myFunc();//现在我决定等待结果,那就是阻塞
我不确定在这种情况下是否需要担心等待处理。也不确定是否需要传入回调。我还认为这构成了一个终结

编辑

以这个结束,

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg)
    {
        IAsyncResult asyncResult = function.BeginInvoke(arg, iAsyncResult =>
            {
                if (!(iAsyncResult as AsyncResult).EndInvokeCalled)
                {
                    (iAsyncResult.AsyncState as Func<T, R>).EndInvoke(iAsyncResult);
                }

            }, function); 

        return delegate
        {
            WaitHandle.WaitAll(new WaitHandle[] { asyncResult.AsyncWaitHandle }); 

            return function.EndInvoke(asyncResult); 
        };
    }
public static Func HandleInvoke(此函数,T参数)
{
IAsyncResult asyncResult=function.BeginInvoke(arg,IAsyncResult=>
{
if(!(iAsyncResult as AsyncResult).endInvokeCall)
{
(iAsyncResult.AsyncState为Func).EndInvoke(iAsyncResult);
}
},功能);
返回代表
{
WaitHandle.WaitAll(新的WaitHandle[]{asyncResult.AsyncWaitHandle});
返回函数.EndInvoke(asyncResult);
};
}
这似乎很有效。回调函数检查是否已调用EndInvoke,如果未调用,则调用EndInvoke。否则,将在返回的委托中调用EndInvoke

第二版

这是我的最新尝试——还没有向我抛出任何错误,而且似乎处理得很好。我无法让它在委托返回函数.EndInvoke()结果的情况下工作,但委托会等到匿名回调中调用了EndInvoke后再返回R.Thread.Sleep()可能不是最佳解决方案。还可以使用更多的检查来确保在每种情况下都实际分配了R

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg)
    {
        R r = default(R); 

        IAsyncResult asyncResult = function.BeginInvoke(arg, result =>
            {
                r = (result.AsyncState as Func<T, R>).EndInvoke(result);

            }, function); 


        return delegate
        {
            while (!(asyncResult as AsyncResult).EndInvokeCalled)
            {
                Thread.Sleep(1); 
            }

            return r; 
        };
    }
public static Func HandleInvoke(此函数,T参数)
{
R=默认值(R);
IAsyncResult asyncResult=function.BeginInvoke(参数,结果=>
{
r=(result.asynchState为Func).EndInvoke(result);
},功能);
返回代表
{
while(!(asyncResult作为asyncResult).endInvokeCall)
{
睡眠(1);
}
返回r;
};
}

这应该行得通,但我对设计不感兴趣。。。这是基本问题

如果调用了myFunc,则不应在回调中调用EndInvoke,但如果不调用myFunc,因为不关心返回值,则必须在回调中调用EndInvoke。这使得使用API变得不明显,并且容易出错

有了你在那里的睡眠,它是活泼的,尽管它不太可能经常咬你。这将使用适当的同步原语来保证一切都将以正确的顺序发生。这是未经测试的代码,但应该可以工作

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg)
    {
        R retv = default(R);
        bool completed = false;

        object sync = new object();

        IAsyncResult asyncResult = function.BeginInvoke(arg, 
            iAsyncResult =>
            {
                lock(sync)
                {
                    completed = true;
                    retv = function.EndInvoke(iAsyncResult);
                    Monitor.Pulse(sync); // wake a waiting thread is there is one
                }
            }
            , null);

        return delegate
        {

            lock (sync)
            {
                if (!completed) // if not called before the callback completed
                {
                    Monitor.Wait(sync); // wait for it to pulse the sync object
                }
                return retv;
            }
        };
    }
public static Func HandleInvoke(此函数,T参数)
{
R retv=默认值(R);
bool completed=false;
对象同步=新对象();
IAsyncResult asyncResult=function.BeginInvoke(arg,
iAsyncResult=>
{
锁定(同步)
{
完成=正确;
retv=function.EndInvoke(iAsyncResult);
Monitor.Pulse(sync);//唤醒正在等待的线程是否存在
}
}
,空);
返回代表
{
锁定(同步)
{
if(!completed)//如果在回调完成之前未调用
{
Monitor.Wait(sync);//等待它脉冲同步对象
}
返回电视;
}
};
}

我发现了一种更简单的方法,可以在调用站点而不是后台线程中抛出异常(重新抛出):

    public static Func<R> Future<T, R>(this Func<T, R> func, T arg)
    {
        IAsyncResult result = func.BeginInvoke(arg, null, null);

        return () => func.EndInvoke(result);
    }
public static Func Func Future(此Func Func,T参数)
{
IAsyncResult result=func.BeginInvoke(arg,null,null);
return()=>func.EndInvoke(结果);
}
对于一个动作来说几乎是一样的:

    public static Action Future<T>(this Action<T> action, T arg)
    {
        IAsyncResult result = action.BeginInvoke(arg, null, null);

        return () => action.EndInvoke(result);
    }
公共静态动作未来(此动作,T参数)
{
IAsyncResult result=action.BeginInvoke(arg,null,null);
return()=>action.EndInvoke(结果);
}

但是如果您不需要结果,那么ThreadPool.QueueUserWorkItem()会更有效。

我明白了……因为我不认为EndInvoke总是被调用,您是说?没错。您的更新更好,但您也可以捕获
函数
。添加了另一个编辑,但我意识到它也不能完全正常工作。除了调用EndInvoke()之外,还有什么方法可以获得该方法的结果吗?因为根据操作的不同,EndInvoke()可能会被调用两次,这会引发运行时错误。实际上,以一种不泄漏和不争用的方式使回调可选工作有点棘手。我可以在答案中发布代码,如果你想的话。。。如果你想自己解决这个问题,请查看
System.Monitor
我做了另一次尝试(第二次编辑),它没有抛出任何错误,但我想它可能是泄漏的。如果你有一个更好的解决方案,那就太酷了。如果你能使用.NET4,那么这个任务类值得一看:(可能比你自己的东西更好)
    public static Action Future<T>(this Action<T> action, T arg)
    {
        IAsyncResult result = action.BeginInvoke(arg, null, null);

        return () => action.EndInvoke(result);
    }