简体   繁体   中英

Simple LOCK and ThreadPoolQuestion for WP7 (C#)


funny, I was going to test tombstoning and concurrency, and during my simple setup, I thought what the heck, is it really worth the trouble. But now, after 45 minutes of setting up some stupid test classes, I ran into the first error I don't understand.

Seems I need a little bit more practice in lists, locks and threads. Does anyone know why this throws an illegal operation exception (see attached code).

For one who likes the F5 experience better, here is the complete solution (300kB)
http://www.filesavr.com/TXXXFVE40GTJK43

Do not open the views, they might crash your VS2010. And you need the WP7 tools, sorry, even though I am pretty sure this example would work (not work) on pure C# also.

[EDIT] I update the link, now it is working (with the old code)
And I found the first bug, thanks to the comment.

This works:

    private void IncOneWithLock()
    {
        lock (CounterListOne)
        {
            IncListOne();
        }
    }

    private void IncListOne()
    {
        if (CounterListOne == null)
        {
            Log("CounterListOne == null");
            return;
        }

        var c = 0;
        var oldList = CounterListOne.ToList();
        foreach (var i in oldList)
        {
            CounterListOne[c++] = i + 1;
            Thread.Sleep(Next(80*DelayFactor, 150*DelayFactor));
        }
    }

Will keep testing tombstoning stuff and post possible questions in later thread. Changing list while iterating - hello newbie mistake :-)

[/EDIT]

For your conveniece, the expection occurs in this function, and its an invalid operation expection:

 private void IncOneWithLock()
    {
        if (CounterListOne == null)
        {
            Log("CounterListOne == null");
            return;
        }

        lock (this)
        {
            var c = 0;
            foreach (var i in CounterListOne)
            {
                CounterListOne[c++] = i + 1;
                Thread.Sleep(Next(80 * DelayFactor, 150 * DelayFactor));
            }
        }
    }

Here is the full source of the test class:

public class CounterClass : TestBase
{
    private DispatcherTimer _dT;
    public int CounterA { get; set; }

    public ObservableCollection<int> CounterListOne { get; set; }
    public List<int> CounterListTwo { get; set; }
    public List<int> CounterListThree { get; set; }
    private const int DelayFactor = 10;


    public CounterClass()
    {
        CounterListOne = new ObservableCollection<int>();
        CounterListTwo = new List<int>();
        CounterListThree = new List<int>();

        InitCounterLists();
        StartBackgroundLogger();
    }

    public void LogLists()
    {
        lock (this)
            //lock (CounterListTwo)
             //   lock (CounterListThree)
                {
                    Log("====================================================");
                    Log("CounterListOne   " + String.Join("-", CounterListOne.Select(x => x.ToString()).ToArray()));
                    Log("CounterListTwo   " + String.Join("-", CounterListTwo.Select(x => x.ToString()).ToArray()));
                    Log("CounterListThree " + String.Join("-", CounterListThree.Select(x => x.ToString()).ToArray()));
                    Log("====================================================");
                }
    }

    public void RunTests()
    {
        Log("MultiIncWithoutLocks");
        //MultiIncWithoutLocks();

        Log("MultiIncWithLocks");
        MultiIncWithLocks();
    }

    public void MultiIncWithoutLocks()
    {
        ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
    }

    public void MultiIncWithLocks()
    {
        ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
        ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
    }

    private void IncOneWithoutLock()
    {
        var c = 0;
        foreach (var i in CounterListOne)
        {
            CounterListOne[c++] = i+1;
            Thread.Sleep(Next(80 * DelayFactor, 150 * DelayFactor));
        }
    }

    private void IncOneWithLock()
    {
        if (CounterListOne == null)
        {
            Log("CounterListOne == null");
            return;
        }

        lock (this)
        {
            var c = 0;
            foreach (var i in CounterListOne)
            {
                CounterListOne[c++] = i + 1;
                Thread.Sleep(Next(80 * DelayFactor, 150 * DelayFactor));
            }
        }
    }

    private void InitCounterLists()
    {
        InitCounterOne();
        InitCounterTwo();
        InitCounterThree();
    }


    private void InitCounterOne()
    {
        for (int i = 0; i < Next(1, 5); i++)
        {
            CounterListOne.Add(0);
        }
    }

    private void InitCounterTwo()
    {
        for (int i = 0; i < Next(1, 5); i++)
        {
            CounterListTwo.Add(0);
        }
    }

    private void InitCounterThree()
    {
        for (int i = 0; i < Next(1, 5); i++)
        {
            CounterListThree.Add(0);
        }
    }

    private void StartBackgroundLogger()
    {
         _dT = new DispatcherTimer();
         _dT.Tick += (a,b) => LogLists();
        _dT.Interval = new TimeSpan(0,0,0,3);
        _dT.Start();
    }


}

You should provide more detailed description of the exception. It is connected with foreach and CounterListOne: you get it changed while iterating on its values, that results to InvalidOperationException .

You are using the observable collection to bind your view against right?
What is happening here is that by updating the values in the observablecollection you are essentailly trying to update the UI through a non UI thread which results in your exception. Is you use the Dispatcher.BeginInvoke to update the values instead it will work

Dispatcher.BeginInvoke( () => { // This code is on the UI thread. });

Unfortunately this will sort or ruin part of your test as it offloads all the value updating activities back to the UI. A way around this would be to create a custom Iobservable element. You can store and update the values in that object when you want and manually invoke it's Updated event (through the Dispatcher.BeginInvoke ) when you want. That at least puts the updating of values wholly on seperate threads. But due to the way Silverlight visualisation is arranged you can never update the UI from a seprate thread

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM