Silverlight 中的多线程 - 如何处理回调?
我在我的 silverlight 应用程序中遇到了困难的情况。我的客户必须从服务中检索一次三个查找表。一旦所有三个集合全部检索完毕,申请将继续。为简单起见,我仅显示一个名为“国家/地区”的查找表的代码。
我正在考虑在不同的线程上运行每个服务调用,并利用 ManualResetEvent[] 和 WaitHandle.WaitAll() 来同步线程。
但由于 Silverlight 的异步特性,服务调用使用回调来告诉调用者单个集合现已加载。我不能简单地让这个工作并且很困惑。
这是检索国家/地区的服务调用: 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 线程上运行,代码和回调将正常工作。但是如果我进行多线程操作,我的线程就会卡住。查看代码注释
我做错了什么?我的策略有意义吗?
高度赞赏,
I have come across a difficult situation in my silverlight app. My client has to retrieve once three lookup tables from the Service. Once all three collections are fully retrieved the application shall continue. For simplicity I only show the code for one lookup table called "Countries".
I was thinking to run each service call on a different thread and utilize the ManualResetEvent[] and WaitHandle.WaitAll() to sync the threads.
But due the async nature of Silverlight the Service call uses a callback to tell the caller that a single collection has now been loaded. I can't simply get this working and am confused.
This is the service call to retrieve the Countries:
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);
}
This is the code I am running to call the service above:
Controller Class:
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);
}
Update:
I have updated the code to make it more clear, the code and callbacks are working if ran on the UI thread. But my threads get stuck if I did this multithreaded. see code comments
What am I doing wrong? Does my strategy makes sense?
Highly appreciated,
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
从您的代码中尚不完全清楚,但您似乎试图通过执行
WaitHandle.WaitAll
来阻止主 Silverlight 线程。在 Silverligt 中你不能这样做。相反,您应该通过回调更新用户界面的内容。您可以通过多种方式做到这一点,并且使用数据绑定可能会对您有所帮助。
您可以使用数据绑定到视图并通过独立回调更新的视图模型。只有当执行第三个回调时,视图模型才会完全更新。这可以通过视图模型上的“就绪”属性来表示,该属性可用于隐藏和显示部分用户界面。
如果您想“阻止”应用程序,直到检索到所有数据,您将必须显示一个繁忙指示器(例如在 Silverlight 工具包)。当检索完所有数据后,您应该隐藏忙碌指示器。
如果您希望像现在一样处理服务,则需要从后台线程或任务调用服务。然后,该线程/任务可以按照与代码中所示相同的方式阻塞,除非您在后台线程而不是主线程上执行此操作。当所有三个回调都执行完毕后,您可以从后台线程/任务更新用户界面。
如果您更喜欢使用线程而不是任务,我建议您使用线程池中的线程,而不是创建自己的线程。
如果您不熟悉 Silverlight 编程模型的异步特性,我建议您通过示例代码来了解如何通过单个服务调用更新用户界面。然后,您可以按照与现在相同的方式将其扩展到许多服务调用。
It is not entirely clear from your code, but it seems that you are trying to block the main Silverlight thread by doing a
WaitHandle.WaitAll
. You cannot do that in Silverligt.Instead you should update the contents of you user interface from the callbacks. You can do that in multiple ways and using data-binding may help you.
You can use a view-model that is databound to a view and is updated by independent callbacks. Only when the third callback is executed will the view-model be fully updated. That can be signaled by a "ready" property on the view-model that can be used to hide and show parts of the user interface.
If you want to "block" the application until all data has been retrieved you will have to display a busy indicator (for instance found in the Silverlight Toolkit). You should then hide the busy indicator when all data has been retrieved.
If you prefer to handle the service as you do now you need to call the services from a background thread or task. This thread/task can then block in the same way as you have shown in your code except you do it on a background thread and not the main thread. When all three callbacks have executed can you update the user interface from the background thread/task.
If you prefer to use a thread and not a task I suggest that you use a thread from the threadpool instead of creating your own.
If you are new to the asynchronous nature of the Silverlight programming model I suggest that you work your way through sample code to understand how to update the user interface from a single service call. You can then extend that to many service calls in the same manner as you are doing now.
好吧,我认为您可能必须探索视图模型模式才能了解它应该如何处理这种情况,但如果我理解正确,您问题的答案可能是以下策略。
忘记阻塞主线程 - 它不起作用,您引入了一个显示的等待元素&虽然显示其他程序元素不可访问,例如,直到指示所有对象已加载的某个值为 true 为止,并且它将在单独的线程中计算,这将依次启动 3 个线程并阻塞,直到所有 3 个线程都加载完毕。
或者,如果您使用更清晰的 ViewModel 方法,您将引入将加载的所有 3 个元素组合的属性,例如
在 FirstLoaded、SecondLoaded 和 ThirdLoaded 的 get 方法中,您执行集合的异步加载,一旦加载它们,您就为它们进行 NotifyPropertyChanged 并检查当所有 3 个都设置为 true 时,是否需要通知 AllLoaded 也为 true。一旦 AllLoaded 设置为 true,您的等待元素就会隐藏,程序元素就可以工作。
这种方式更加MVVM& Silverlightish,但如果你不在主 SL 线程上执行块,你的方法也可以工作。
Okay I think you might have to explore View Model pattern to understand how it is supposed to handle such situations, but if I understand correctly the answer to your question could be the following strategy.
Forget blocking main thread - it doesn't work, you introduce a wait element that is shown & while it is shown other program elements arent accessible for example, until certain value indicating that all objects are loaded is true, and it will be computed in a separate thread, that in turn will launch 3 threads and will block until all 3 are loaded.
Or if you use more clear ViewModel approach you introduce property that will be combination of all 3 elements loaded, like
and in get methods for those FirstLoaded,SecondLoaded and ThirdLoaded you perform asynchronous load of collections, once they are loaded you NotifyPropertyChanged for them and check whether you need to notify that AllLoaded is true too , when all 3 are set to true. Once AllLoaded is set to true, your wait element hides and program elements become available to work.
This approach is more MVVM & Silverlightish but your approach can work too if you dont perform block on main SL thread.
我正在做类似的事情,但是使用 ThreadPool.QueueUserWorkItem 我加载了 3 个 Scans、Actions 和 Steps 对象的集合。
在 Loader 类中,我注册了来自 3 个实体的所有回调,并在那里设置了手动重置事件,如下所示:
在加载器内部,我使用 ThreadPool 等待另一个线程内的所有回调,如下所示:
这种方式对我来说效果很好,我对其进行了基准测试而且性能比按顺序执行要好得多。
回家这有帮助。问候。
Im doing something similar but using the ThreadPool.QueueUserWorkItem I load 3 collections of Scans, Actions, and Steps objects.
in the Loader class I register all the callbacks from the 3 entities and there I set the manualresetevents like this:
Inside the loader I wait for all inside another thread using the ThreadPool like this:
This way is working fine for me, I have benchmark it and the performances is much better than doing that in a sequential order.
home this helps. regards.