Threaded application issue
Ive got an issue where the first item in the collection is responding to an update but no others (out of 40). Ive had a look around the net for answers but unfortunately Im still getting nowhere after a couple of days.
The calling code which kicks off a thread for the detection loop:
_detectionThread = new Thread(() => _x.StartDetection());
_detectionThread.Start();
Ive got the following code in one of my helper classes which simply polls and when something is detected, by way of an event the View-Model is called:
public event EventHandler SomethingIsDetected;
private void OnSomethingDetected()
{
if (SomethingIsDetected!= null)
{
SomethingIsDetected(this, new EventArgs());
}
}
Code for detection loop:
var startCheckTime = DateTime.Now;
var nextCheck = startCheckTime.AddSeconds(PollingInterval.TotalSeconds);
while (_performDetection)
{
startCheckTime = DateTime.Now;
if (startCheckTime >= nextCheck)
开发者_Go百科 {
nextCheck = startCheckTime.AddSeconds(PollingInterval.TotalSeconds);
{
var detectionTask = Task.Factory.StartNew(() => IsXConnected());
IsXPresent = detectionTask.Result;
Thread.Sleep(TimeSpan.FromSeconds(1));
if (IsXPresent)
{
Application.Current.Dispatcher.Invoke(new Action(OnSomethingDetected));
}
}
}
Thread.Sleep(10);
}
Code for updating items. View is bound to properties here (especially CurrentItem). Items is an ObservableCollection
foreach (var item in Items) //loop through 40 items
{
//do some operation then set the current item
Application.Current.Dispatcher.Invoke(new Action(() => CurrentItem = item));
}
While Im stepping through (with the help of a debugging converter) I notice that the item is udpated just the first time. The rest just loops through. Ive setup property CurrentItem with a DependencyProperty.
Ive tried using CheckAccess to use Delegate and udpate property and that didnt help either.
Any help is welcome and thanks!
Your problem has nothing to do with multi-threading, it has to with how closures capture variables in your last code snippet. You lamba's all share the same variable, that is there is a single item
variable. Since your lamda's run after the end of the loop item
will always be set to the the last item in the Items collection. (Although they could get run with any item, depending on exactly when then run)
The compiler will convert:
foreach (var item in Items) //loop through 40 items
{
//do some operation then set the current item
Application.Current.Dispatcher.Invoke(new Action(() => CurrentItem = item));
}
to something morally equivalend to this:
class closuseCapture {
private ItemType itemCapture;
public void Loop() {
foreach (var item in Items) //loop through 40 items
{
itemCapture = item;
//do some operation then set the current item
Application.Current.Dispatcher.Invoke(new Action(ActionMethod));
}
}
public void ActionMethod() {
CurrentItem = itemCapture;
}
The fix is to declare a variable inside your loop so each interaction of the loop gets its own copy of the item:
foreach (var item in Items) //loop through 40 items
{
var localItem = item;
//do some operation then set the current item
Application.Current.Dispatcher.Invoke(new Action(() => CurrentItem = localItem ));
}
See any or all of these for more information or do a Google search for "Access to modified closure"
http://devnet.jetbrains.net/thread/273042
Access to Modified Closure
Access to Modified Closure (2)
http://weblogs.asp.net/fbouma/archive/2009/06/25/linq-beware-of-the-access-to-modified-closure-demon.aspx
精彩评论