Потокобезовасный List
От: help-me  
Дата: 11.03.14 09:51
Оценка: :)
Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком. Сейчас у меня такая проблема, что даже обычный список без всяких lock работает потокобезопасно 0_0 (хотя, он не должен быть потокобезопасным). Как такое может быть? Здесь что-то не так?
Вот мой код, которым я проверял список , получая к нему доступ из разных потоков

    class Program
    {
        static List<string> store = new List<string>(100);
        static void Main(string[] args)
        {
            //TEST
            List<string> newList = new List<string>(2);
            for (int i = 0; i < 10; i++)
            {
                string s = "";
                for (int k = 0; k < i; k++)
                {
                    s += " ";
                }
                for (int k = 0; k < 10; k++)
                {
                    newList.Add(s + k.ToString());
                }
            }
            store.AddRange(newList);


            Thread[] thrs = new Thread[5];
            List<List<string>> ress = new List<List<string>>();
            for (int i = 0; i < 5; i++)
            {
                thrs[i] = new Thread(() => { ress.Add(GetCompanies2()); });
            }
            for (int i = 0; i < 5; i++)
            {
                thrs[i].Start();
            }
            Console.Read();
            foreach (var res in ress)
            {
                res.ForEach(k => Console.WriteLine(k));
                Console.WriteLine("end---------------------");
            }
            Console.Read();
            Console.Read();
        }

        //TEST
        public static List<string> GetCompanies2()
        {
            List<string> aaa = new List<string>();
            foreach (var VARIABLE in store)
            {
                Thread.Sleep(10);
                aaa.Add(VARIABLE);
            }
            return aaa;
        }
    }

я даже поставил Thread.Sleep(10), чтобы убеждаться, что каждый поток не выполнит проход по foreach быстрее, чем запустятся другие потоки. По идее, каждый поток должен двигать Ienumerable.MoveNext в foreach и Ienumerable.Current должен отдавать не всегда последовательные значения из списка, но почему-то у меня всегда получается в консоли такая идеально ровная лесенка (каждая ступень из 10 элементов) http://screencast.com/t/hW7Z9n8T .
Если бы потоки двигали Ienumerable.MoveNext асинхронно, то эта лесенка должна была бы искривляться. Но почему это не происходит?
Re: Потокобезовасный List
От: Аноним  
Дата: 11.03.14 10:02
Оценка:
Здравствуйте, help-me, Вы писали:

HM>Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком. Сейчас у меня такая проблема, что даже обычный список без всяких lock работает потокобезопасно 0_0 (хотя, он не должен быть потокобезопасным). Как такое может быть? Здесь что-то не так?


Вероятность события : мала
Re[2]: Потокобезовасный List
От: help-me  
Дата: 11.03.14 10:20
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, help-me, Вы писали:


HM>>Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком. Сейчас у меня такая проблема, что даже обычный список без всяких lock работает потокобезопасно 0_0 (хотя, он не должен быть потокобезопасным). Как такое может быть? Здесь что-то не так?


А>Вероятность события : мала


Как переделать код, чтобы вероятности срабатывали? Почему недостаточно юзать Thread.Sleep(10) или Thread.Sleep(100)? Кстати, в дебаггере бряк на Thread.Sleep(10) срабатывает из разных потоков в произвольном порядке, то есть, часть данных должна пропускаться и лесенка должна была искривляться , но почему-то не искривляется.
Re: Потокобезовасный List
От: hardcase Пират http://nemerle.org
Дата: 11.03.14 10:44
Оценка: +1
Здравствуйте, help-me, Вы писали:

HM>Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком.


Я не знаю, что тестирует приведенный код, но вот мой наглядно показывает проблемы:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

class Program
{
  static void Main(string[] args)
  {
    const int threadCount = 2;
    var buffer = new List<int>();

    var threads = new Thread[threadCount];
    for(var i = 0; i < threads.Length; ++i)
      threads[i] = new Thread(AddItems);

    for (var i = 0; i < threads.Length; ++i)
      threads[i].Start(buffer);

    for (var i = 0; i < threads.Length; ++i)
      threads[i].Join();

    for (int i = 0, j = 1; j < buffer.Count; ++i, ++j)
    {
      if (buffer[i] + 1 != buffer[j])
      {
        Console.WriteLine("fail");
        return;
      }
    }
    Console.WriteLine("success");
  }

  public static void AddItems(object arg)
  {
    var buffer = (List<int>) arg;
    for(var i = 0; i < 200; ++i)
    {
      //lock (buffer)
        buffer.Add(buffer.Count);
    }
  }
}


Достаточно раскомментировать lock(buffer) и программа перестанет вылетать.
/* иЗвиНите зА неРовнЫй поЧерК */
Re: Потокобезовасный List
От: namespace  
Дата: 11.03.14 10:52
Оценка:
HM>должен отдавать не всегда последовательные значения из списка
С чего бы это вдруг? Объект store изменился между двумя обращениями к нему? Нет.
Указатель на следующий элемент хранится не в самом списке, в механизме foreach.
А попробовать в одном потоке читать (с задержкой), а в другом добавлять?, и в третьем удалять.
Re[3]: Потокобезовасный List
От: Аноним  
Дата: 11.03.14 11:14
Оценка:
Здравствуйте, help-me, Вы писали:

HM>Здравствуйте, Аноним, Вы писали:


А>>Здравствуйте, help-me, Вы писали:


HM>>>Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком. Сейчас у меня такая проблема, что даже обычный список без всяких lock работает потокобезопасно 0_0 (хотя, он не должен быть потокобезопасным). Как такое может быть? Здесь что-то не так?


А>>Вероятность события : мала


HM>Как переделать код, чтобы вероятности срабатывали? Почему недостаточно юзать Thread.Sleep(10) или Thread.Sleep(100)? Кстати, в дебаггере бряк на Thread.Sleep(10) срабатывает из разных потоков в произвольном порядке, то есть, часть данных должна пропускаться и лесенка должна была искривляться , но почему-то не искривляется.


если вероятность события мала, то воспроизвести событие можно увеличив количество испытаний.
Re: Потокобезовасный List
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.03.14 11:38
Оценка:
Здравствуйте, help-me, Вы писали:

HM>Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком.


Обычный список + ReaderWriterLock(Slim)
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Потокобезовасный List
От: help-me  
Дата: 11.03.14 14:27
Оценка: :)
Здравствуйте, namespace, Вы писали:

N>Указатель на следующий элемент хранится не в самом списке, в механизме foreach.


Это с чего вдруг в механизме foreach ? форыч получает IEnumerator, у которого есть MoveNext() чтобы двигаться на следующий элемент , и Current, чтобы получить текущий элемент. Указатель на следующий элемент должен явно храниться в IEnumerator, чтобы IEnumerator мог отдавать нужный Current. Если 2 разных потока будут читать одновременно список в форыче, то первый может сдвинуть MoveNext(), сразу за ним второй поток сдвинет MoveNext(), а первый поток только после этого полезет доставать Current и в итоге должен пропустить 1 элемент. Не так что ли?
Re[2]: Потокобезовасный List
От: help-me  
Дата: 11.03.14 16:53
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, help-me, Вы писали:


HM>>Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком.


AVK>Обычный список + ReaderWriterLock(Slim)


http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlockslim%28v=vs.110%29.aspx
тут в примере юзается нестатический ReaderWriterLockSlim для нестатического Dictionary
у меня класс статический и List в нем тоже статический. Понятно, что мне нужно юзать статический ReaderWriterLockSlim . но не будет ли каких-нибудь глюков, связанных с тем, что я заюзаю статический ReaderWriterLockSlim? Или лучше переписать класс и List на экземплярные?
Re[3]: Потокобезовасный List
От: help-me  
Дата: 11.03.14 16:58
Оценка:
Здравствуйте, help-me, Вы писали:

HM>Здравствуйте, AndrewVK, Вы писали:


AVK>>Здравствуйте, help-me, Вы писали:


HM>>>Мне нужно написать потокобезопасный список, либо как-то работать потокобезопасно с обычным списком.


AVK>>Обычный список + ReaderWriterLock(Slim)


HM>http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlockslim%28v=vs.110%29.aspx

HM>тут в примере юзается нестатический ReaderWriterLockSlim для нестатического Dictionary
HM>у меня класс статический и List в нем тоже статический. Понятно, что мне нужно юзать статический ReaderWriterLockSlim . но не будет ли каких-нибудь глюков, связанных с тем, что я заюзаю статический ReaderWriterLockSlim? Или лучше переписать класс и List на экземплярные?

А , еще забыл. Мне удобнее у меня в коде вызывать EnterWriteLock или EnterReadLock иp разных классов, то есть, мне нужно сделать объект ReaderWriterLockSlim публичным (в примере он приватный). Можно ли его сделать публичным, чтобы можно было его лочить из разных классов? А если можно, то это является плохой практикой и так все-равно лучше не делать?
Re[3]: Потокобезовасный List
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.03.14 18:42
Оценка:
Здравствуйте, help-me, Вы писали:

HM>но не будет ли каких-нибудь глюков, связанных с тем, что я заюзаю статический ReaderWriterLockSlim?


Не будет, если не накосячишь.

HM> Или лучше переписать класс и List на экземплярные?


Может и лучше, я ж подробностей не знаю.
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[4]: Потокобезовасный List
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.03.14 18:42
Оценка:
Здравствуйте, help-me, Вы писали:

HM>А , еще забыл. Мне удобнее у меня в коде вызывать EnterWriteLock или EnterReadLock иp разных классов, то есть, мне нужно сделать объект ReaderWriterLockSlim публичным (в примере он приватный).


Не стоит. Лучше все подробности синхронизации завернуть в один класс.
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: Потокобезовасный List
От: Sinclair Россия https://github.com/evilguest/
Дата: 12.03.14 05:29
Оценка: +1
Здравствуйте, help-me, Вы писали:
HM>Это с чего вдруг в механизме foreach ? форыч получает IEnumerator, у которого есть MoveNext() чтобы двигаться на следующий элемент , и Current, чтобы получить текущий элемент. Указатель на следующий элемент должен явно храниться в IEnumerator, чтобы IEnumerator мог отдавать нужный Current.
Пока что правильно.
HM>Если 2 разных потока будут читать одновременно список в форыче, то первый может сдвинуть MoveNext(), сразу за ним второй поток сдвинет MoveNext(), а первый поток только после этого полезет доставать Current и в итоге должен пропустить 1 элемент. Не так что ли?
Конечно нет. Каждый поток независимо друг от друга вызывает GetEnumerator, поэтому экземпляры IEnumerator у них разные.
И MoveNext они вызывают у разных экземпляров, поэтому и не возникает никаких гонок.

Иначе бы foreach не работал бы даже в одном потоке:

var items = new int[] {1, 2, 3, 4, 5)
foreach(a in items)
  foreach(b in items) // <-- вот здесь _тот же_ список пробегается сначала до конца,
     Console.WriteLine(a*b); // и это не мешает внешнему циклу спокойно делать свою работу

В этом примере есть два енумератора — для a и для b. В вашем примере енумераторов пять, по одному на каждый VARIABLE.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.