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