Шаг 244.
Язык программирования C#. Начала.
Многопоточное программирование. Использование потоков

    На этом шаге мы рассмотрим пример использования потоков.

    Начиная с этого шага мы рассмотрим несколько примеров, в которых используются потоки. В примере, представленном ниже, два дочерних потока заполняют символьный массив. Один из потоков заполняет массив с конца до начала, присваивая элементам массива кириллические буквы. Второй поток заполняет массив с начала и до конца, присваивая элементам массива латинские буквы. Потоки работают, пока не "встретятся" где-то посредине массива. Рассмотрим представленную далее программу.

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, выполняются следующие команды:

    Если же при проверке условия second>first оно окажется ложным, то в else-блоке условной конструкции командой

  Thread.CurrentThread.Abort(); 
завершается выполнение потока. Здесь мы с помощью статического свойства CurrentThread класса Thread получили ссылку на объект потока, в котором выполняется метод, и из этого объекта вызвали метод Abort(), в результате чего поток прекращает работу.

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

    Оба потока выполняются до тех пор, пока значение переменной first меньше значения переменной second. Значение переменной first увеличивается в процессе заполнения массива, а значение переменной second уменьшается. Параметры программы подобраны так, что наиболее вероятный сценарий следующий: в какой-то момент значения переменных first и second станут одинаковыми, и потоки завершат свое выполнение. При этом элемент, индекс которого равен совпадающим значениям переменных first и second, останется со старым значением - ни один из потоков не присвоит новое значение этому элементу.

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




Предыдущий шаг Содержание Следующий шаг