您如何等待 AppDomain 在 C# 中处理异步回调,然后 return 结果?
How can you wait on AppDomain to process async callback in C# and then return the results?
我有一些代码可以加载并且 AppDomain(称之为域)调用域内的对象函数。目的是使用设备 API 从 usb 设备获取项目列表以检索信息。 API 需要回调 return 信息。
var AppDomain.CreateDomain(
$"BiometricsDomain{System.IO.Path.GetRandomFileName()}");
var proxy = domain.CreateInstanceAndUnwrap(proxy.Assembly.FullName, proxy.FullName
?? throw new InvalidOperationException()) as Proxy;
var ids = obj.GetIdentifications();
加载到域中的代理代码如下
public class Proxy : MarshalByRefObject
{
public List<String> GetIdentifications()
{
var control = new R100DeviceControl();
control.OnUserDB += Control_OnUserDB;
control.Open();
int nResult = control.DownloadUserDB(out int count);
// need to be able to return the list here but obviously that is not
// going to work.
}
private void Control_OnUserDB(List<String> result)
{
// Get the list of string from here
}
}
有什么办法可以在设备上等待,return调用回调时根据需要的信息?由于 GetIdentifications()
已经 returned 我不知道如何获得
注意:虽然对于异步处理的问题可能有更优雅的解决方案,但这种情况发生在子 AppDomain 中这一事实保证了子 AppDomain 的最佳实践。 (见下面的链接)
即
- 不允许在父域中执行用于子 AppDomain 的代码
- 不允许复杂类型冒泡到父 AppDomain
- 不允许异常以自定义异常类型的形式跨越 AppDomain 边界
OP:
I am using it for fault tolerance
首先,我可能会添加一个 Open
或类似的方法来为数据实现提供时间。
var proxy = domain.CreateInstanceAndUnwrap(proxy.Assembly.FullName, proxy.FullName
?? throw new InvalidOperationException()) as Proxy;
proxy.Open(); // <------ new method here
.
. some time later
.
var ids = obj.GetIdentifications();
然后在您的代理中进行这些更改以允许在后台进行数据处理,以便在您调用 GetNotifications
时数据可能已准备就绪。
public class Proxy : MarshalByRefObject
{
ConcurrentBag<string> _results = new ConcurrentBag<string>();
public void Open()
{
var control = new R100DeviceControl();
control.OnUserDB += Control_OnUserDB;
control.Open();
// you may need to store nResult and count in a field?
nResult = control.DownloadUserDB(out int count);
}
public List<String> GetIdentifications()
{
var copy = new List<string>();
while (_results.TryTake(out var x))
{
copy.Add(x);
}
return copy;
}
private void Control_OnUserDB(List<String> result)
{
// Get the list of string from here
_results.Add (result);
}
}
现在,您可能会改进 GetNotifications
,以便在数据准备好之前调用 GetNotifications
或在后续数据到达之前调用它时接受超时。
更多
您可以考虑使用 TaskCompletionSource<TResult>
将基于事件的异步模式 (EAP) 操作包装为一项任务,以便可以等待事件。
public class Proxy : MarshalByRefObject {
public List<String> GetIdentifications() {
var task = GetIdentificationsAsync();
return task.Result;
}
private Task<List<String>> GetIdentificationsAsync() {
var tcs = new TaskCompletionSource<List<string>>();
try {
var control = new R100DeviceControl();
Action<List<string>> handler = null;
handler = result => {
// Once event raised then set the
// Result property on the underlying Task.
control.OnUserDB -= handler;//optional to unsubscribe from event
tcs.TrySetResult(result);
};
control.OnUserDB += handler;
control.Open();
int count = 0;
//call async event
int nResult = control.DownloadUserDB(out count);
} catch (Exception ex) {
//Bubble the error up to be handled by calling client
tcs.TrySetException(ex);
}
// Return the underlying Task. The client code
// waits on the Result property, and handles exceptions
// in the try-catch block there.
return tcs.Task;
}
}
您还可以通过添加使用 CancellationToken
取消比预期更长的回调的能力来改进它。
然后可以等待代理
List<string> ids = proxy.GetIdentifications();
不知道你为什么不保持一点状态,然后在调用中等待结果:
public class Proxy : MarshalByRefObject
{
bool runningCommand;
int lastResult;
R100DeviceControl DeviceControl { get{ if(deviceControl == null){ deviceControl = new R100DeviceControl(); deviceControl.OnUserDB += Control_OnUserDB; } return deviceControl; } }
public List<String> GetIdentifications()
{
if(runningCommand) return null;
DeviceControl.Open();
runningCommand = true;
lastResult = control.DownloadUserDB(out int count);
}
private void Control_OnUserDB(List<String> result)
{
runningCommand = false;
// Get the list of string from here
}
}
一旦你有了这样的模式,你就可以轻松地在异步和其他模式之间切换,而之前它看起来有点难以理解,因为你集成了异步逻辑,这样你就可以实现同步方法,然后进行异步如果您愿意,可以包装。
我有一些代码可以加载并且 AppDomain(称之为域)调用域内的对象函数。目的是使用设备 API 从 usb 设备获取项目列表以检索信息。 API 需要回调 return 信息。
var AppDomain.CreateDomain(
$"BiometricsDomain{System.IO.Path.GetRandomFileName()}");
var proxy = domain.CreateInstanceAndUnwrap(proxy.Assembly.FullName, proxy.FullName
?? throw new InvalidOperationException()) as Proxy;
var ids = obj.GetIdentifications();
加载到域中的代理代码如下
public class Proxy : MarshalByRefObject
{
public List<String> GetIdentifications()
{
var control = new R100DeviceControl();
control.OnUserDB += Control_OnUserDB;
control.Open();
int nResult = control.DownloadUserDB(out int count);
// need to be able to return the list here but obviously that is not
// going to work.
}
private void Control_OnUserDB(List<String> result)
{
// Get the list of string from here
}
}
有什么办法可以在设备上等待,return调用回调时根据需要的信息?由于 GetIdentifications()
已经 returned 我不知道如何获得
注意:虽然对于异步处理的问题可能有更优雅的解决方案,但这种情况发生在子 AppDomain 中这一事实保证了子 AppDomain 的最佳实践。 (见下面的链接)
即
- 不允许在父域中执行用于子 AppDomain 的代码
- 不允许复杂类型冒泡到父 AppDomain
- 不允许异常以自定义异常类型的形式跨越 AppDomain 边界
OP:
I am using it for fault tolerance
首先,我可能会添加一个 Open
或类似的方法来为数据实现提供时间。
var proxy = domain.CreateInstanceAndUnwrap(proxy.Assembly.FullName, proxy.FullName
?? throw new InvalidOperationException()) as Proxy;
proxy.Open(); // <------ new method here
.
. some time later
.
var ids = obj.GetIdentifications();
然后在您的代理中进行这些更改以允许在后台进行数据处理,以便在您调用 GetNotifications
时数据可能已准备就绪。
public class Proxy : MarshalByRefObject
{
ConcurrentBag<string> _results = new ConcurrentBag<string>();
public void Open()
{
var control = new R100DeviceControl();
control.OnUserDB += Control_OnUserDB;
control.Open();
// you may need to store nResult and count in a field?
nResult = control.DownloadUserDB(out int count);
}
public List<String> GetIdentifications()
{
var copy = new List<string>();
while (_results.TryTake(out var x))
{
copy.Add(x);
}
return copy;
}
private void Control_OnUserDB(List<String> result)
{
// Get the list of string from here
_results.Add (result);
}
}
现在,您可能会改进 GetNotifications
,以便在数据准备好之前调用 GetNotifications
或在后续数据到达之前调用它时接受超时。
更多
您可以考虑使用 TaskCompletionSource<TResult>
将基于事件的异步模式 (EAP) 操作包装为一项任务,以便可以等待事件。
public class Proxy : MarshalByRefObject {
public List<String> GetIdentifications() {
var task = GetIdentificationsAsync();
return task.Result;
}
private Task<List<String>> GetIdentificationsAsync() {
var tcs = new TaskCompletionSource<List<string>>();
try {
var control = new R100DeviceControl();
Action<List<string>> handler = null;
handler = result => {
// Once event raised then set the
// Result property on the underlying Task.
control.OnUserDB -= handler;//optional to unsubscribe from event
tcs.TrySetResult(result);
};
control.OnUserDB += handler;
control.Open();
int count = 0;
//call async event
int nResult = control.DownloadUserDB(out count);
} catch (Exception ex) {
//Bubble the error up to be handled by calling client
tcs.TrySetException(ex);
}
// Return the underlying Task. The client code
// waits on the Result property, and handles exceptions
// in the try-catch block there.
return tcs.Task;
}
}
您还可以通过添加使用 CancellationToken
取消比预期更长的回调的能力来改进它。
然后可以等待代理
List<string> ids = proxy.GetIdentifications();
不知道你为什么不保持一点状态,然后在调用中等待结果:
public class Proxy : MarshalByRefObject
{
bool runningCommand;
int lastResult;
R100DeviceControl DeviceControl { get{ if(deviceControl == null){ deviceControl = new R100DeviceControl(); deviceControl.OnUserDB += Control_OnUserDB; } return deviceControl; } }
public List<String> GetIdentifications()
{
if(runningCommand) return null;
DeviceControl.Open();
runningCommand = true;
lastResult = control.DownloadUserDB(out int count);
}
private void Control_OnUserDB(List<String> result)
{
runningCommand = false;
// Get the list of string from here
}
}
一旦你有了这样的模式,你就可以轻松地在异步和其他模式之间切换,而之前它看起来有点难以理解,因为你集成了异步逻辑,这样你就可以实现同步方法,然后进行异步如果您愿意,可以包装。