На этом шаге мы рассмотрим пример использования потоков.
Начиная с этого шага мы рассмотрим несколько примеров, в которых используются потоки. В примере, представленном ниже, два дочерних потока заполняют символьный массив. Один из потоков заполняет массив с конца до начала, присваивая элементам массива кириллические буквы. Второй поток заполняет массив с начала и до конца, присваивая элементам массива латинские буквы. Потоки работают, пока не "встретятся" где-то посредине массива. Рассмотрим представленную далее программу.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace pr244_1 { class Program { // Главный метод: static void Main() { // Размер массива: int size = 20; // Создание массива: char[] symbs = new char[size]; // Заполнение массива "звездочками": for(int k=0; k < symbs.Length; k++) { symbs[k] = '*'; Console.Write("|" + symbs[k]); } Console.WriteLine("|"); // Индекс первого и последнего элемента в массиве: int first = 0, second = symbs.Length-1; // Объектные переменные для потоков: Thread A,B; // Создание объекта для первого потока: A = new Thread(()=> { // Начальный символ для заполнения массива: char start = 'Я'; // Бесконечный цикл: while(true) { // Если второй индекс больше первого индекса: if (second > first) { // Значение элемента: symbs[second] = start; // Новый символ для присваивания: start--; // Новое значение индекса: second--; // Задержка в выполнении потока: Thread.Sleep(100); } // Если второй индекс не больше первого: else { // Завершение в выполнении потока: Thread.CurrentThread.Abort(); } } }); // Создание объекта для второго потока: B = new Thread(()=> { // Начальный символ для заполнения массива: char start = 'A'; // Бесконечный цикл: while (true) { // Если первый индекс меньше второго: if (first < second) { // Значение элемента: symbs[first] = start; // Новый символ для присваивания: start++; // Новое значение индекса: first++; // Задержка в выполнении потока: Thread.Sleep(100); } // Если первый индекс не меньше второго: else { // Завершение выполнения потока: Thread.CurrentThread.Abort(); } } }); // Запуск первого потока на выполнение: A.Start(); // Запуск второго потока на выполнение: B.Start(); // Ожидание выполнения первого потока: if (A.IsAlive) A.Join(); // Ожидание выполнения второго потока: if(B.IsAlive) B.Join(); // Отображение содержимого массива: for(int k = 0; k < symbs.Length; k++) { Console.Write("|" + symbs[k]); } Console.WriteLine("|"); } } }
Результат выполнения программы может быть таким, как показано ниже:
Рис.1. Результат работы приложения
После создания символьного массива symbs он заполняется символом '*' (звездочка). В целочисленную переменную first записывается значение первого элемента массива, а в переменную second записывается значение последнего элемента массива. Первый поток создается на основе лямбда-выражения, определяющего метод, который выполняется при запуске первого потока на выполнение. В этом методе символьная переменная start получает начальное значение 'Я', после чего запускается формально бесконечный цикл. В теле цикла, при истинности условия second>first, выполняются следующие команды:
symbs[second] = start;
start--;
second--;
Thread.Sleep(100);
Если же при проверке условия second>first оно окажется ложным, то в else-блоке условной конструкции командой
Thread.CurrentThread.Abort();
Второй поток выполняется похожим образом, но элементы заполняются с начала до конца, и для заполнения используются латинские буквы.
Оба потока выполняются до тех пор, пока значение переменной first меньше значения переменной second. Значение переменной first увеличивается в процессе заполнения массива, а значение переменной second уменьшается. Параметры программы подобраны так, что наиболее вероятный сценарий следующий: в какой-то момент значения переменных first и second станут одинаковыми, и потоки завершат свое выполнение. При этом элемент, индекс которого равен совпадающим значениям переменных first и second, останется со старым значением - ни один из потоков не присвоит новое значение этому элементу.
На следующем шаге мы продолжим приводить примеры использования потоков.