代码之家  ›  专栏  ›  技术社区  ›  jwize

如何在AppDomain上等待处理C中的异步回调,然后返回结果?

  •  2
  • jwize  · 技术社区  · 7 年前

    我有一些加载的代码,AppDomain(称为domain)调用域中的对象函数。目的是使用设备API从usb设备获取项目列表以检索信息。API需要回调来返回信息。

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

    GetIdentifications() 已经回来了我不知道怎么弄到

    3 回复  |  直到 7 年前
        1
  •  2
  •   Nkosi    7 年前

    通过使用 TaskCompletionSource<TResult>

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

    How to: Wrap EAP Patterns in a Task

        2
  •  2
  •   Deleted    7 年前

    注意 :虽然对于异步处理的问题可能有更优雅的解决方案,但发生在子AppDomain中的事实证明了子AppDomain的最佳实践。(见下面的链接)

    1. 不允许在父域中执行用于子AppDomain的代码
    2. 不允许复杂类型冒泡到父AppDomain
    3. 不允许异常以自定义异常类型的形式跨越AppDomain边界

    我用它来容错

    首先我可能会加上 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);
        }
    }
    

    现在你也许可以改进 在事件中接受超时 获取通知 在数据准备好之前调用,或者在后续数据到达之前调用乘法。

        3
  •  0
  •   Jay    7 年前

    不知道为什么不保持一个小状态,然后在调用中等待结果:

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