Silverlight中的多线程 - 如何处理回调?

时间:2011-05-22 01:01:54

标签: multithreading silverlight silverlight-4.0 callback

我在Silverlight应用程序中遇到了困难的情况。我的客户端必须从服务中检索三次查找表。一旦完全检索到所有三个集合,应用程序将继续。为简单起见,我只显示一个名为“Countries”的查找表的代码。

我想在另一个线程上运行每个服务调用,并使用ManualResetEvent []和WaitHandle.WaitAll()来同步线程。

但是由于Silverlight的异步特性,Service调用使用回调来告诉调用者现在已经加载了单个集合。我不能简单地让这个工作并且感到困惑。

这是检索国家/地区的服务电话: 的 SCMService:

    private LoadOperation<Country> _allCountriesLoadOperation;
    private Action<ObservableCollection<Country>> _getAllCountriesCallBack;

    public void GetAllCountries(Action<ObservableCollection<Country>> getAllCountriesCallback)
    {
          Context.Countries.Clear();
          var query = Context.GetCountriesQuery().OrderBy(c => c.CountryName);
          _getAllCountriesCallBack = getAllCountriesCallback;
          _allCountriesLoadOperation = Context.Load(query);
          _allCountriesLoadOperation.Completed += OnLoadCountriesCompleted;
    }

    private void OnLoadCountriesCompleted(object sender, EventArgs e)
    {
          _allCountriesLoadOperation.Completed -= OnLoadCountriesCompleted;
          var countries = new EntityList<Country>(Context.Countries, _allCountriesLoadOperation.Entities);
          _getAllCountriesCallBack(countries);
    }

这是我正在运行以调用上述服务的代码: 控制器类:

        public void OnGetAllCountries()
        {
            _service.GetAllCountries(GetCountriesCallback);
        }

        private void GetCountriesCallback(ObservableCollection<Country> countries)
        {
            if (countries != null)
            {
                if (Countries == null)
                {
                    Countries = countries;
                    _manualResetEvents[0].Set();
                }
            }
        }

        Controller()
        {
             //OnGetAllCountries();  This line alone would work fine. But I need to                    
             //wait and make sure the callback is finished before continuing. 
             // Hence my multi threading approach below:

             new Thread(OnGetAllCountries).Start();
             WaitHandle.WaitAll(_manualResetEvents);
        }

更新 我已经更新了代码以使其更清晰,如果在UI线程上运行代码和回调正在运行。但是如果我做了这个多线程,我的线程就会卡住。看代码评论

我做错了什么?我的策略是否有意义?

非常感谢,

3 个答案:

答案 0 :(得分:0)

您的代码并不完全清楚,但似乎您试图通过执行WaitHandle.WaitAll来阻止主Silverlight线程。你不能在Silverligt那样做。

相反,您应该从回调中更新用户界面的内容。您可以通过多种方式实现这一目标,使用数据绑定可能对您有所帮助。

您可以使用数据绑定到视图的视图模型,并通过独立回调进行更新。只有在执行第三个回调时,视图模型才会完全更新。这可以通过视图模型上的“就绪”属性发出信号,该属性可用于隐藏和显示用户界面的各个部分。

如果要在检索完所有数据之前“阻止”应用程序,则必须显示忙碌指示符(例如,在Silverlight Toolkit中找到)。然后,您应该在检索完所有数据后隐藏忙碌指示符。

如果您希望像现在一样处理服务,则需要从后台线程或任务调用服务。然后,此线程/任务可以以与您在代码中显示的方式相同的方式阻止,除非您在后台线程而不是主线程上执行此操作。当所有三个回调都已执行时,您可以从后台线程/任务更新用户界面。

如果您更喜欢使用线程而不是任务,我建议您使用线程池中的线程而不是创建自己的线程。

如果您不熟悉Silverlight编程模型的异步特性,我建议您通过示例代码了解如何从单个服务调用更新用户界面。然后,您可以按照与现在相同的方式将其扩展到许多服务调用。

答案 1 :(得分:0)

好的我认为您可能需要探索View Model模式以了解它应该如何处理这种情况,但如果我理解正确,您的问题的答案可能是以下策略。

忘记阻塞主线程 - 它不起作用,你引入一个显示的等待元素&amp;虽然显示其他程序元素不可访问,例如,直到指示所有对象都被加载的某个值为真,并且它将在一个单独的线程中计算,然后它将启动3个线程并将阻塞直到加载所有3个。

或者,如果您使用更清晰的ViewModel方法,则引入将加载的所有3个元素的组合的属性,例如

AllLoaded { get{return FirstLoaded && SecondLoaded && ThirdLoaded}} , 

并在这些FirstLoaded,SecondLoaded和ThirdLoaded的get方法中执行异步加载的集合,一旦它们加载了NotifyPropertyChanged,并检查是否需要通知AllLoaded也是,当所有3都设置为true时。一旦AllLoaded设置为true,您的wait元素就会隐藏,程序元素可以工作。

这种方法更多MVVM&amp; Silverlightish,但如果你不对主SL线程执行阻止,你的方法也可以工作。

答案 2 :(得分:0)

我正在做类似的事情,但使用ThreadPool.QueueUserWorkItem我加载了3个Scans,Actions和Steps对象集合。

  1. 通过使用ThreadPool拉动一个实体的类在一个单独的线程中执行它。
  2. 这个类应该有一个公共回调来通知加载器任务完成。
  3. 我有与我必须拉的实体一样多的类。
  4. 在Loader类中我注册了3个实体的所有回调,并在那里设置了像这样的manualresetevents:

        void Scans_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ScanItems")
            {
                Debug.WriteLine("Scans Done =====");     
                manualEvents[(int)ItemDone.Scans].Set(); 
            }
        }
    

    在加载器内部,我使用ThreadPool在另一个线程内等待所有内容:

            manualEvents = new ManualResetEvent[3];
            manualEvents[(int)ItemDone.Scans] = new ManualResetEvent(false);
            manualEvents[(int)ItemDone.Actions] = new ManualResetEvent(false);
            manualEvents[(int)ItemDone.AssySteps] = new ManualResetEvent(false);
    
            ThreadPool.QueueUserWorkItem((obj) =>
            {
                Scans.Get(WipID, WC);
                Actions.Get(WipID, WC);
                AssySteps.Get(WipID, WC);
                if (WaitHandle.WaitAll(manualEvents, new TimeSpan(0, 0, 5)))
                {
                    int InstIndex = 0;
                    Instructions = new List<Instruction>();
                    AssySteps.AssyStepsItems.ForEach(s =>{Some code here....});
                }
            }
    

    这种方式对我来说工作正常,我已经对它进行了基准测试,并且表现要好于按顺序执行。 这有帮助。问候。

相关问题