На этом шаге мы рассмотрим еще один пример использования потоков.
В следующей программе создается двумерный числовой массив, и этот массив построчно заполняется. Заполнение каждой строки выполняется отдельным потоком. Объектные переменные, через которые реализуются ссылки на потоки, организованы в массив. Это общая идея. Далее рассмотрим способ ее реализации. Интересующий нас программный код представлен в тексте ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace pr245_1 { class Program { // Главный метод: static void Main() { // Двумерный массив: int[,] nums = new int[6, 9]; // Массив из объектных переменных для потоков: Thread[] t = new Thread[nums.GetLength(0)]; // Перебор элементов массива: for(int i = 0; i < t.Length; i++) { // Локальная переменная для цикла: int p = i; // Создание объекта потока: t[i] = new Thread(()=> { // Перебор элементов в строке // целочисленного массива: for(int j = 0; j < nums.GetLength(1); j++) { // Элементу присваивается значение: nums[p, j] = (p + 1) * (j + 1); // Приостановка выполнения потока: Thread.Sleep(100); } }); // Запуск потока на выполнение: t[i].Start(); } // Ожидание завершения дочерних потоков: for(int i = 0; i < t.Length; i++) { if (t[i].IsAlive) t[i].Join(); } // Отображение содержимого двумерного целочисленного // массива: for (int i = 0; i < nums.GetLength(0); i++) { for (int j = 0; j < nums.GetLength(1); j++) { Console.Write("{0,-4}", nums[i, j]); } Console.WriteLine(); } } } }
Результат выполнения программы представлен ниже:
Рис.1. Результат работы приложения
Мы создаем двумерный целочисленный массив nums. Еще мы создаем массив t из объектных переменных класса Thread. Размер массива t равен количеству строк в массиве nums (вычисляется инструкцией nums.GetLength(0)). После этого перебираются элементы массива t, и на каждой итерации цикла при фиксированном индексе i выполняются такие команды:
nums[p, j] = (p+1)*(j + 1);
Thread.Sleep(100);
t[i].Start();
Таким образом, после выполнения конструкции цикла будут запущены дочерние потоки. Количество дочерних потоков равно количеству строк в двумерном массиве. Ссылки на объекты потоков записаны в массив t. Каждый поток заполняет одну строку двумерного массива.
Далее по плану содержимое массива должно отображаться в консольном окне. Но для этого главный поток должен дождаться окончания выполнения дочерних потоков. Поэтому запускается конструкция цикла, в которой перебираются все элементы из массива t. На каждой итерации цикла для данного индекса i проверяется условие t[i].IsAlive. Если оно истинно (поток продолжает работу), то ожидается завершение потока (команда
t[i].Join();
На следующем шаге мы продолжим изучение этого вопроса.