Проблема в многопоточной программе

Дано задание которое звучит следующим образом:

С клавиатуры вводится число роботов, собирающих игрушки, и число наборов для сборки. Число наборов для сборки игрушек должно превышать число роботов не менее чем в 100 раз. Каждый робот собирает игрушку за некоторое время, попадающее в диапазон, введенный с клавиатуры пользователем. Когда игрушка собрана, робот берет новый набор для сборки игрушки, пока все наборы не будут собраны. Каждый робот работает в отдельном потоке.

Требования:

В ListBox необходимо выводить информацию о ходе процесса. События, которые необходимо отразить в ListBox:

  1. робот №n начал сбор набора №m. Время сбора набора: x минут;
  2. робот №n закончил сбор набора №m;
  3. наборы для сборки закончились.

Код:

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public bool stop;
        List<Thread> threads = new List<Thread>();
        int[] robots;
        int numberSets = 0;
        Random random = new Random();
        object locker = new object();
        //List<int> sets = new List<int>();
        //List<int> setss;
        public Form1()
        {
            InitializeComponent();
            stop = false;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach (Thread thread in threads) 
            {
                thread.Abort();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (Convert.ToInt32(numberRobots.Text) * 100 <= Convert.ToInt32(numberSet.Text))
            {
                robots = new int[Convert.ToInt32(numberRobots.Text)];
                for (int i = 0; i < Convert.ToInt32(numberRobots.Text); i++)
                {
                    robots[i] = i + 1;
                }
                for (int i = 0; i < Convert.ToInt32(numberRobots.Text); i++)
                {
                    threads.Add(new Thread(new ParameterizedThreadStart(CreatSet)));
                    threads[i].Name = $"Робот {i + 1}";
                    threads[i].Start(Convert.ToInt32(robots[i]));
                }              
            }
            else
            {
                Console.WriteLine("Неправильно!");
            }
        }

        public void CreatSet(object obj)
        {            
            int numberRobot = (int)obj;

            while (numberSets != Convert.ToInt32(numberSet.Text))
            {                
                lock (locker)
                {
                    numberSets++;
                    infAssembly.Items.Add($"• {Thread.CurrentThread.Name} начал сбор набора №{numberSets}. Время сбора набора: {random.Next(50, 100)} секунд;"); //<-- Ошибка
                }
                infAssembly.Items.Add($"• {Thread.CurrentThread.Name} закончил сбор набора №{numberSets}.");
                Thread.Sleep(random.Next(50, 100));
            }  
                
            infAssembly.Items.Add("•Наборы для сборки закончились.");
        }
    }
}

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

Возникающее исключение:

System.InvalidOperationException: "Недопустимая операция в нескольких потоках: попытка доступа к элементу управления `infAssembly` не из того потока, в котором он был создан"

введите сюда описание изображения


Ответы (1 шт):

Автор решения: SurfaceStack

Чтобы исправить ошибку Попытка доступа не из того потока.... можно использовать Invoke (вот вопрос с объяснением).

Используется Invoke если надо установить/передать какое-либо значение элементу, получать значения можно без него.

В вашем случае использование такое (переменную message создаю отдельно, чтобы получить правильное Thread.CurrentThread.Name):

...

var message = $"• {Thread.CurrentThread.Name} начал сбор набора №{numberSets}. Время сбора набора: {random.Next(50, 100)} секунд;";
Invoke((MethodInvoker)delegate
{
    infAssembly.Items.Add(message);
});

...
→ Ссылка