none
Гарантировано обеспечить нескольким потокам по одному элементу из списка. RRS feed

  • Вопрос

  • Работаю с потоками и сделал тестовое приложение которое создает список из 20 элементов прогресс баров и некоторое количество потоков с функцией которое дает доступ прогресс бару из списка, после чего производится заполнение полученного элемента.

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

    private List<ProgressBar> lpb = new List<ProgressBar>(); private List<Thread> lth = new List<Thread>(); private int startpointX = 12; private int startpoinTY = 12; public Form1() { //Создаем прогресс бары for (int i = 0; i < 20; i++) { ProgressBar pb = new ProgressBar(); pb.Location = new System.Drawing.Point(startpointX, startpoinTY); pb.Name = "pb" + i; startpoinTY += 30; pb.Size = new System.Drawing.Size(350, 23); pb.Maximum = 10000; pb.Step = 1; lpb.Add(pb); } //добавляем в список контроллеров foreach (var pb in lpb) { this.Controls.Add(pb); } //создаем список потоков for (int i = 0; i < 2; i++) { Thread thr = new Thread(func); lth.Add(thr); } //стартуем потоки foreach (var th in lth) { th.Start(); } InitializeComponent(); } private void func() { while (lpb.Count > 0) { ProgressBar pb = lpb.First(); lpb.RemoveAt(0); Label txt = new Label(); txt.Text = Thread.CurrentThread.ManagedThreadId.ToString(); txt.Location = new Point(pb.Location.X +350, pb.Location.Y+6); txt.Name = "txt" + lpb.Count; while (pb.Value < 10000) { if (pb.InvokeRequired) { pb.Invoke(new Action(delegate() {

    if (pb.Value == 0) this.Controls.Add(txt); pb.PerformStep(); })); } } } }

    иногда при старте наблюдались моменты когда два потока работают с первым прогресс баром, а второй просто пустует уже до конца алгоритма.





    • Изменено Magals 22 марта 2017 г. 15:21
    22 марта 2017 г. 15:01

Ответы

Все ответы

  • Для начала перестаньте создавать элементы UI на рабочих потоках. Все операции с ними от создания до разрушения должны выполняться исключительно на основном потоке.

    This posting is provided "AS IS" with no warranties, and confers no rights.

    22 марта 2017 г. 16:42
    Модератор
  • Для начала перестаньте создавать элементы UI на рабочих потоках. Все операции с ними от создания до разрушения должны выполняться исключительно на основном потоке.

    This posting is provided "AS IS" with no warranties, and confers no rights.


    label добавил по быстрому только для идентификации
    23 марта 2017 г. 7:14
  • Уберите. Потом добавьте lock(lpb) при обращении к данному объекту на потоках.

    This posting is provided "AS IS" with no warranties, and confers no rights.

    23 марта 2017 г. 15:37
    Модератор
  • Добрый день.

    При работе с коллекциями в многопоточном режиме можно воспользоваться классами из пространства имен System.Collections.Concurrent.

    24 марта 2017 г. 5:33
    Отвечающий
  • Сделал собственный класс с контролем к переменой.

    добавил lock, но кажется я неправильно им пользуюсь.

    internal class ControlReadElementOfList : List<Pblbl>
        {
            private int index = -1;
    
            public new Pblbl Read()
            {
                return this.index >= this.Count ? new Pblbl(null, null) : base[this.index];
            }
    
            public bool Next()
            {
                index++;
                return this.index < this.Count;
            }
    
            public void ClearIndex()
            {
                this.index = -1;
            }
        }
    
        internal struct Pblbl
        {
            public Pblbl(ProgressBar _pb, Label _lbl)
            {
                this.PbUnit = _pb;
                this.Lbl = _lbl;
            }
    
            public ProgressBar PbUnit { get; }
            public Label Lbl { get; }
        }
    
        public partial class Form1 : Form
        {
            private readonly List<Thread> _lth = new List<Thread>();
            private readonly int _startpointX = 12;
            private readonly int _startpoinTy = 12;
            private readonly ControlReadElementOfList _elist = new ControlReadElementOfList();
            
    
            public Form1()
            {
                //Создаем прогресс бары
                for (int i = 0; i < 20; i++)
                {
                    ProgressBar pb = new ProgressBar
                    {
                        Location = new System.Drawing.Point(_startpointX, _startpoinTy),
                        Name = "pb" + i
                    };
                    _startpoinTy += 30;
                    pb.Size = new System.Drawing.Size(350, 23);
                    pb.Maximum = 10000;
                    pb.Step = 1;
    
                    Label txt = new Label
                    {
                        Location = new Point(pb.Location.X + 350, pb.Location.Y + 6),
                        Name = "txt" + _elist.Count
                    };
                    _elist.Add(new Pblbl(pb, txt));
                }
                //добавляем в список контроллеров
                foreach (var pb in _elist)
                {
                    this.Controls.Add(pb.PbUnit);
                    this.Controls.Add(pb.Lbl);
                }
    
                //создаем список потоков 
                for (int i = 0; i < 5; i++)
                {
                    Thread thr = new Thread(func) {IsBackground = false};
                    _lth.Add(thr);
                }
    
                //стартуем потоки
                foreach (var th in _lth)
                {
                    th.Start();
                }
    
    
    
                InitializeComponent();
            }
    
            private void func()
            {
                while (_elist.Next())
                {
                    lock (_elist.Read().PbUnit)
                    {
                        ProgressBar pb = _elist.Read().PbUnit;
                        Label lbl = _elist.Read().Lbl;
    
                        while (pb.Value < pb.Maximum)
                        {
                            if (pb.InvokeRequired)
                            {
                                pb.Invoke(new Action(delegate()
                                {
                                    pb.PerformStep();
                                    lbl.Text = Thread.CurrentThread.ManagedThreadId.ToString();
    
                                    
                                }));
                            }
                        }
                    }
                }
            }
    
        }

    при 5 потоках выходит такая картина

    второй и третий прогресс был пропущен, как раз из за того, что1-2-3 поток обратились к одному элементу.

    и номер потока почему-то один и тот же


    • Изменено Magals 27 марта 2017 г. 13:06
    27 марта 2017 г. 13:05
  • У вас проблемы с гонками при доступе к коллекции. Делайте lock на коллекции:

     lock (_elist)

    27 марта 2017 г. 13:27
    Отвечающий