1

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)
                {
                    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!

slugster
  • 49,403
  • 14
  • 95
  • 145
TheRRRanger
  • 365
  • 3
  • 12
  • 2
    1) Why bother creating a task to invoke IsXConnected since on the very next line you block until the operation is completed. 2) What is IsXConnected doing? Since that is what I take it is returning false after the first result, it seems important. 3) I realize that there is some code missing from the foreach loop that sets CurrentItem = item, but at first blush it doesn't make any sense since unless there is some blocking call in there it's just going to very quickly go through all the Items, and CurrentItem will end up being Items.Last, which will be the only thing the UI will ever see. – Paul Wheeler Jul 07 '11 at 03:30
  • Thanks for that Paul, Ive got Debug.WriteLine in one of the UI updating bits which indicates that only the first item is calling the updating bit. (even after addressing point 1 by just calling the method, without a Task). – TheRRRanger Jul 07 '11 at 04:57
  • I believe the execution of thread from constructor of MainWindow.XAML.cs was the culprit. Checkout [this](http://stackoverflow.com/questions/6639237/wpf-custom-control-dependencyproperty-issue) post for more info. – TheRRRanger Jul 10 '11 at 06:58

1 Answers1

3

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

Community
  • 1
  • 1
shf301
  • 31,086
  • 2
  • 52
  • 86
  • Thanks for that @shf301. Tried declaring a local variable and unfortunately that didnt quite help either. I'll see what else I can do...(Also read up on stuff youve placed a link to). Cheers. – TheRRRanger Jul 07 '11 at 04:58