【问题标题】:Silverlight - limit application to one WCF call at a timeSilverlight - 一次将应用程序限制为一个 WCF 调用
【发布时间】:2011-07-26 11:42:24
【问题描述】:

Silverlight 一次只能同时发送一定数量的 WCF 请求。我正在尝试序列化我的应用程序的特定部分正在执行的请求,因为我不需要它们同时运行。

问题如下(总结如下): “Silverlight 应用程序中的 WCF 代理使用启动 Web 服务调用的线程的 SynchronizationContext 来安排在收到响应时调用异步事件处理程序。当从 Silverlight 应用程序的 UI 线程启动 Web 服务调用时,异步事件处理程序代码也将在 UI 线程上执行。” http://tomasz.janczuk.org/2009/08/improving-performance-of-concurrent-wcf.html

总结:基本上,如果你阻塞了调用异步方法的线程,它就永远不会被调用。

我无法找出正确的线程模型,这样才能以合理的方式给我想要的东西。

我唯一的其他要求是我不希望 UI 线程阻塞。

据我所知,应该工作的是,如果 UI 线程有一个工作线程,它将调用作为Action 委托排队,然后使用AutoResetEvent 执行任务在另一个工作线程中一次一个。有两个问题: 1)调用 async 的线程不能阻塞,因为那样 async 永远不会被调用。实际上,如果您将该线程放入等待循环中,我注意到它也不会被调用 2)您需要一种方法来从异步调用的完成方法发出信号,表明它已完成。

抱歉,拖了这么久,感谢阅读。有什么想法吗?

【问题讨论】:

  • Silverlight 5 显然具有更好的服务调用线程性能,因此您可能正在为不再存在的问题创建解决方法。
  • 真的吗?我没听说过,但很高兴知道。不幸的是,要切换到 SL 5 还需要一段时间。
  • 顺便问一下,您是否有链接或知道您从何处获得此信息?我想看看你在说什么,但找不到。

标签: multithreading silverlight wcf asynchronous


【解决方案1】:

我使用了一个我自己构建的类来同步执行加载操作。使用该类,您可以注册不同域上下文的多个加载操作,然后一一执行。当所有操作完成(成功或失败)时,您可以向被调用的类的构造函数提供一个 Action。

这是课程的代码。我认为它不完整,您必须对其进行更改以符合您的期望。也许它可以帮助您解决您的情况。

  public class DomainContextQueryLoader {
    private List<LoadOperation> _failedOperations;
    private Action<DomainContextQueryLoader> _completeAction;
    private List<QueuedQuery> _pendingQueries = new List<QueuedQuery>();

    public DomainContextQueryLoader(Action<DomainContextQueryLoader> completeAction) {
        if (completeAction == null) {
            throw new ArgumentNullException("completeAction", "completeAction is null.");
        }
        this._completeAction = completeAction;
    }

    /// <summary>
    /// Expose the count of failed operations
    /// </summary>
    public int FailedOperationCount {
        get {
            if (_failedOperations == null) {
                return 0;
            }
            return _failedOperations.Count;
        }
    }

    /// <summary>
    /// Expose an enumerator for all of the failed operations
    /// </summary>
    public IList<LoadOperation> FailedOperations {
        get {
            if (_failedOperations == null) {
                _failedOperations = new List<LoadOperation>();
            }
            return _failedOperations;
        }
    }

    public IEnumerable<QueuedQuery> QueuedQueries {
        get {
            return _pendingQueries;
        }
    }

    public bool IsExecuting {
        get;
        private set;
    }

    public void EnqueueQuery<T>(DomainContext context, EntityQuery<T> query) where T : Entity {
        if (IsExecuting) {
            throw new InvalidOperationException("Query cannot be queued, cause execution of queries is in progress");
        }
        var loadBatch = new QueuedQuery() {
            Callback = null,
            Context = context,
            Query = query,
            LoadOption = LoadBehavior.KeepCurrent,
            UserState = null
        };

        _pendingQueries.Add(loadBatch);
    }

    public void ExecuteQueries() {
        if (IsExecuting) {
            throw new InvalidOperationException("Executing of queries is in progress");
        }

        if (_pendingQueries.Count == 0) {
            throw new InvalidOperationException("No queries are queued to execute");
        }

        IsExecuting = true;
        var query = DequeueQuery();
        ExecuteQuery(query);
    }

    private void ExecuteQuery(QueuedQuery query) {
        System.Diagnostics.Debug.WriteLine("Load data {0}", query.Query.EntityType);
        var loadOperation = query.Load();
        loadOperation.Completed += new EventHandler(OnOperationCompleted);
    }

    private QueuedQuery DequeueQuery() {
        var query = _pendingQueries[0];
        _pendingQueries.RemoveAt(0);
        return query;
    }

    private void OnOperationCompleted(object sender, EventArgs e) {
        LoadOperation loadOperation = sender as LoadOperation;
        loadOperation.Completed -= new EventHandler(OnOperationCompleted);

        if (loadOperation.HasError) {
            FailedOperations.Add(loadOperation);
        }

        if (_pendingQueries.Count > 0) {
            var query = DequeueQuery();
            ExecuteQuery(query);
        }
        else {
            IsExecuting = false;
            System.Diagnostics.Debug.WriteLine("All data loaded");
            if (_completeAction != null) {
                _completeAction(this);
                _completeAction = null;
            }
        }
    }

}

更新: 我刚刚注意到您没有使用 WCF RIA 服务,所以这门课可能对您没有帮助。

【讨论】:

  • 没错,我没有 WCF RIA 服务,但无论如何,这是一本有趣的书。谢谢。
【解决方案2】:

有一些选择:

- 您可以通过检查 Agatha-rrsl 的实现或仅使用它而不是纯 wcf 来查看 Agatha-rrsl。该框架允许您对请求进行排队。你可以阅读更多here。 - 另一种选择是使用 Reactive 扩展。有一个 SO 示例 here 和更多信息 herehere。 - 您可以尝试 Jeffrey Richter 的 Power Thread 库。他在他的书 CLR via C# 中对此进行了描述。你可以找到图书馆here。这个webcast 给你一些关于它的信息。 - 您可以随时推出自己的实现。 yield 语句在这里是一个很好的帮助。错误处理使获得正确的解决方案变得非常困难。

【讨论】:

    猜你喜欢
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多