Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.60/15: Рейтинг темы: голосов - 15, средняя оценка - 4.60
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
1

ThreadPool - не все потоки учитываются при использовании в цикле

26.12.2015, 18:00. Показов 3072. Ответов 29

Author24 — интернет-сервис помощи студентам
При выполнении ThreadPool в цикле, после определённого количества циклов он зависает на строке 17(Monitor.Wait(workerLocker).
Точнее зависания как такового нет, он просто ждёт workerLocker, но не получает его. При этом runningWorkers = 1 (или2), где runningWorkers косвенно указывает на количество работающих потоков, которые должны бы были освободить workerLocker, но судя по всему они как то пропадают
Подскажите пожалуйста более надёжный способ выяснить, есть ли рабочие потоки в ThreadPool (и их количество), или, как решить вопрос с "пропадающими потоками" при использовании в цикле.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    class Program
    {
        static object workerLocker = new object();
        static int runningWorkers = 10;
        static int counter = 0;
 
        public static void Main()
        {
            for (int j = 0; j < 1000000; j++)
            {
                runningWorkers = 10;
                for (int i = 0; i < runningWorkers; i++)
                    ThreadPool.QueueUserWorkItem(Go, i);
 
                lock (workerLocker)
                  while (runningWorkers > 0)
                        Monitor.Wait(workerLocker);
            }
            Console.WriteLine("Готово! " + "Потоки выполнялись " + counter + " раз");
            Console.ReadLine();
        }
 
        public static void Go(object instance)
        {
            lock (workerLocker)
            {
                counter++;
                runningWorkers--;
                Monitor.Pulse(workerLocker);
            }
        }
    }
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
26.12.2015, 18:00
Ответы с готовыми решениями:

Как в ThreadPool проверить, что все потоки завершены
Как можно проверить, что все потоки завершили свою работу в ThreadPool, а после выполнить...

Необходимо синхронизировать потоки (написать свой ThreadPool)
Надо написать свой ThreadPool. Идея начальная проста: есть очередь задач, которая подаётся на...

Потоки, мультипоточность Task or Async/Await or ThreadPool?
Здравствуйте форумчане. Я очень нуждаюсь в вашей помощи. Я реализовываю парсер с одного довольно...

Определение индекса массива в цикле при использовании указателей
Добрый день! Вопрос, в общем-то, ламерский, но что-то сам дотумкать не могу. Есть код, где...

29
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
30.12.2015, 12:40  [ТС] 21
Author24 — интернет-сервис помощи студентам
OwenGlendower, да, в чистом виде при малом объёме вычислений внутри потока CountdownEvent действительно быстрее. Но в случае если вычислений внутри потока много (как у меня) - то его влияние незначительно...
Но в любом случае он лучше того lock`a который я использовал.

Psilon, спасибо. Похоже, ваш код самый быстрый.
Но я не могу до конца разобраться с Partitioner.
В случае когда мой поток принимает 1 значение при использовании:
C#
1
2
3
4
Parallel.ForEach(source, elementХ =>
{
Console.WriteLine(elementХ);
});
всё просто - в elementХ я имею элемент своего массива.
А как в случае использования Partitioner получить элемент массива или хотя бы его индекс?
0
Master of Orion
Эксперт .NET
6098 / 4954 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
30.12.2015, 13:25 22
Лучший ответ Сообщение было отмечено Andreys5 как решение

Решение

Andreys5, ну в моем примере выше просто
C#
1
2
3
4
5
6
7
public static void Print(this double[] source, int startIndex, int endIndex)
{
   for(int i = startIndex; i < endIndex; i++)
   {
      Console.WriteLine(source[i]);      // че-то делаем с элементами массива
   }
}
То есть метод вызывается на некотором интервале индексов, а уж что с элементами массива делать - уже вы должны разбираться.

Добавлено через 5 минут
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
 
namespace ConsoleApplication29
{
    class Program
    {
        static void Main(string[] args)
        {
            var array = Enumerable.Range(1, 1024).ToArray();
            Print(array);
        }
 
        public static void Print<T>(T[] source)
        {
            Parallel.ForEach(Partitioner.Create(0, source.Length),
                             range =>
                             {
 
                                 Print(source, range.Item1, range.Item2);
                             });
        }
 
        private static void Print<T>(T[] source, int startIndex, int endIndex)
        {
            for (int i = startIndex; i < endIndex; i++)
            {
                Console.WriteLine(source[i]);      // че-то делаем с элементами массива
            }
        }
    }
}
0
aquaMakc
30.12.2015, 15:27
  #23

Не по теме:

Цитата Сообщение от Psilon Посмотреть сообщение
object syncRoot = new object();
...
Цитата Сообщение от Psilon Посмотреть сообщение
lock (syncRoot)
Вот тут я не совсем понял. Зачем захватывать неиспользуемый объект? Я считал, что локом должен захватываться обрабатываемый ресурс, в данном случае result .

0
Storm23
30.12.2015, 15:44
  #24

Не по теме:

Цитата Сообщение от aquaMakc Посмотреть сообщение
Я считал, что локом должен захватываться обрабатываемый ресурс, в данном случае result .
Неа, как раз не рекомендуется лочить объекты данных. По правильному - нужно лочить именно отдельно созданный объект. Более того, стандартные коллекции(List<>, Dictionary<> и т.д.) предоставляют уже готовый объект для этого. А интерфейс ICollection требует что бы вы создали такой объект.

Цитата Сообщение от aquaMakc Посмотреть сообщение
Зачем захватывать неиспользуемый объект?
Не важно используемый он или нет. Важно что его пытаются захватить несколько потоков и он выполняет роль семафора.

0
aquaMakc
30.12.2015, 15:50
  #25

Не по теме:


Цитата Сообщение от Storm23 Посмотреть сообщение
стандартные коллекции(List<>, Dictionary<> и т.д.) предоставляют уже готовый объект для этого
т.е. код типа:
C#
1
2
3
4
5
6
7
8
9
List<TObject> ListName = new List <TObject>();
 
void ThreadWork()
{
    lock (ListName)
    {
       ...
    }
}
должен нормально лочить список для вызвавшего его протока?

0
Storm23
30.12.2015, 16:30
  #26

Не по теме:

Цитата Сообщение от aquaMakc Посмотреть сообщение
должен нормально лочить список для вызвавшего его протока?
Нет. Здесь вы лочите как раз сам ListName. А по правильному нужно так:
C#
1
2
3
4
lock((list as ICollection).SyncRoot)
{
     ...
}

0
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
30.12.2015, 16:40 27
Storm23, оффтопные посты не позволяют ставить "спасибо", так-то благодарю словами

Есть у меня определённая проблема с доступом к List из большого количества потоков. С SyncRoot стало лучше, но нет-нет, да наблюдаю
System.InvalidOperationException: Коллекция была изменена; невозможно выполнить операцию перечисления.
Но это тема отдельного топика.
0
Master of Orion
Эксперт .NET
6098 / 4954 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
30.12.2015, 16:46 28
aquaMakc,
1) лочить нужно неиспользуемую переменную, как уже сказали выше
2) лично я избегаю lock-ов на публичные переменные, потому что вызывающий код уже мог сделать лок на Array.SyncRoot к примеру, и в таком случае я свалюсь в дедлок. Локальная переменная гарантирует то, что её лочить могу только я, и за счёт этого не будет блокировки потоков из-за кода типа
C#
1
2
3
4
5
lock (array.SyncRoot)
{
   var sum = array.Sum();
   Console.WriteLine(sum);
}
3
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
30.12.2015, 17:56  [ТС] 29
Psilon, спасибо, всё работает.
Но после отработки цикла почти вся оперативная память остаётся забитой непонятно чем (массив для расчётов - пуст, результат - всего 1 значение).(такое ощущение что он хранит в памяти все отработанные потоки)
Не подскажите, как с этим бороться?
0
Master of Orion
Эксперт .NET
6098 / 4954 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
30.12.2015, 20:09 30
Andreys5, ну, тут обычно лучше запустить профилировщик и посмотреть, что память занимает. Так-то я сказать не могу Возможно память будет очищена по первому требованию системы (то есть объекты уже умерли, но их еще не собрал сборщик), а возможно где-то висячие ссылки остаются... Это уже нужно разбираться.
0
30.12.2015, 20:09
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.12.2015, 20:09
Помогаю со студенческими работами здесь

Рандомные числа выдают один и тот же результат при их использовании в цикле
Всем доброго времени суток. У меня есть метод, в котором используются рандомные числа, чтобы взять...

При сортировке не учитываются отрицательные элементы
Доброго времени суток! При написании кода возникло 2 проблемы: 1) При сортировке не учитываются...

При компиляции не учитываются изменения, внесенные в проект
Скажите в чём может быть проблема,я запускаю отладку и почему-то вываливается смообщение ,которого...

ссылки с rel=nofollow учитываются при подсчете тиц?
ссылки с rel=nofollow учитываются при подсчете тиц?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru