在单个扩展方法中正确开始和结束调用
我想与其他人仔细检查这是否是创建启动异步进程并返回一个函数的正确方法,该函数在调用时本质上等待该进程并获取结果。
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;
};
}
I want to double check with others whether this would be the correct way to create an extension method that begins an asynchronous process, and returns a function that when invoked essentially waits on that process and gets the 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);
};
}
Essentially I want to use it like such (pseudo code):
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
Wasnt sure if I need to worry about waiting on WaitHandles in this situation or not. Also not sure if passing in a callback would even be necessary. Also I think this constitutes a closure?
EDIT
Ended up with this,
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);
};
}
Which seems to work well. The callback checks if EndInvoke has been called, and if not, calls it. Otherwise EndInvoke is called within the returned delegate.
2ND EDIT
Here is my latest attempt -- hasnt thrown any errors at me yet and seems to handle it well. I couldn't get it to work where the delegate returned the function.EndInvoke() result, but the delegate waits until EndInvoke has been called in the anonymous callback before returning R. Thread.Sleep() probably isnt the best solution, though. Also could use more checking to make sure that R was actually assigned to in each case.
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;
};
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这应该可行,但我不热衷于设计......这是基本问题。
如果调用了 myFunc,则不应在回调中调用 EndInvoke,但如果未调用 myFunc,因为您不关心返回值,则必须在回调中调用 EndInvoke。这使得 API 的使用变得不那么明显并且容易出错。
尽管它不太可能经常咬你,但随着你在那里的睡眠,它很活泼。这使用正确的同步原语来保证一切都会以正确的顺序发生。这是未经测试的代码,但应该可以工作
This should work but I'm not keen on the design... Here is the basic problem.
If myFunc is called than you should not call EndInvoke in the callback but if myFunc is not called, because you don't care about the return value then EndInvoke must be called in the callback. This makes using the API non-obvious and error prone.
with the sleep you have in there it is racy, though it isn't likely to bite you very often. This uses proper synchronization primitives to guarantee that everything will happen in the right order. This is untested code, but should work
我遇到了一种更简单的方法,您可以在调用站点(而不是在后台线程上)(重新)抛出异常:
这对于操作来说几乎是相同的:
但是如果您没有任何需要对于结果,ThreadPool.QueueUserWorkItem() 更有效。
I came across a simpler way of doing it, where you get the benefit of having exceptions (re)throw at the invocation site, instead of on the background thread:
It's nearly identical for an Action:
But if you don't have any need for the result then ThreadPool.QueueUserWorkItem() is more efficient.