3

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

    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);
        };
    }

本质上我想像这样使用它(伪代码):

Func<R> myFunc = (some delegate).HandleInvoke(arg, callback);  
                     // at this point the operation begins, but will be nonblocking

                     // do other stuff

var result = myFunc();   // now I am deciding to wait on the result, which is blocking

不确定我是否需要担心在这种情况下等待 WaitHandles。也不确定是否需要传入回调。我也认为这构成了关闭?

编辑

就这样结束了,

    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); 
        };
    }

这似乎运作良好。回调检查是否已调用 EndInvoke,如果没有,则调用它。否则在返回的委托中调用 EndInvoke。

第二次编辑

这是我最近的尝试——还没有向我抛出任何错误,而且似乎处理得很好。我无法让它在委托返回 function.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; 
        };
    }
4

2 回答 2

2

这应该可行,但我不热衷于设计......这是基本问题。

如果调用了 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;
            }
        };
    }
于 2011-05-23T07:10:57.023 回答
1

我遇到了一种更简单的方法,您可以获得在调用站点而不是在后台线程上抛出异常(重新)抛出的好处:

    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 Action Future<T>(this Action<T> action, T arg)
    {
        IAsyncResult result = action.BeginInvoke(arg, null, null);

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

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

于 2012-08-12T02:59:35.093 回答