Простой LOCK и ThreadPoolQuestion для WP7 (C #) - PullRequest
1 голос
/ 02 марта 2011


Забавно, я собирался протестировать захоронение и параллелизм, и во время своей простой настройки я подумал, какого чёрта, действительно ли это стоит того. Но теперь, после 45 минут настройки некоторых глупых тестовых классов, я столкнулся с первой ошибкой, которую я не понимаю.

Кажется, мне нужно немного больше попрактиковаться в списках, блокировках и потоках. Кто-нибудь знает, почему это вызывает исключение недопустимой операции (см. Прикрепленный код).

Для тех, кому нравится опыт F5, вот полное решение (300 КБ)
http://www.filesavr.com/TXXXFVE40GTJK43

Не открывайте представления, они могут привести к сбою вашего VS2010. И вам нужны инструменты WP7, извините, хотя я почти уверен, что этот пример будет работать (не работать) и на чистом C #.

[EDIT] Я обновляю ссылку, теперь она работает (со старым кодом)
И я нашел первую ошибку, благодаря комментарию.

Это работает:

    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));
        }
    }

Будет продолжать тестирование надгробных материалов и публиковать возможные вопросы в следующей ветке. Изменение списка во время итерации - привет новичку ошибка: -)

[/ EDIT]

Для вашего удобства ожидание происходит в этой функции, и это неправильное ожидание операции:

 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));
            }
        }
    }

Вот полный источник класса теста:

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();
    }


}

Ответы [ 2 ]

3 голосов
/ 02 марта 2011

Вы должны предоставить более подробное описание исключения. Он связан с foreach и CounterListOne: вы меняете его, перебирая его значения, что приводит к InvalidOperationException.

0 голосов
/ 02 марта 2011

Вы используете наблюдаемую коллекцию, чтобы связать свое мнение с правым?
То, что здесь происходит, заключается в том, что, обновляя значения в наблюдаемой коллекции, вы по существу пытаетесь обновить пользовательский интерфейс через не-пользовательский поток, что приводит к вашему исключению. Используете ли вы Dispatcher.BeginInvoke для обновления значений, вместо этого он будет работать

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

К сожалению, это приведет к сортировке или крушению части вашего теста, поскольку он выгружает все действия по обновлению значений обратно в пользовательский интерфейс. Обойти это можно было бы путем создания пользовательского элемента Iobservable. Вы можете сохранять и обновлять значения в этом объекте, когда хотите, и вручную вызывать его событие Updated (через Dispatcher.BeginInvoke), когда хотите. По крайней мере, это ставит обновление значений полностью в отдельных потоках. Но из-за того, как устроена визуализация Silverlight, вы никогда не сможете обновить пользовательский интерфейс из отдельного потока

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...