Потоки

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

Потоки в Windows принадлежат процессам. Программа на C# запускается в единственном потоке, однако процессу может принадлежать множество потоков. Эти потоки могут выполняться одновременно на многопроцессорных системах, что значительно увеличивает производительность программы. В однопроцессорных системах потоки выполняются последовательно, получая по очереди кванты времени ( 20 мс ), однако даже такое выполнение в некоторых случаях приводит к увеличению производительности программы.


Создание и запуск потока

Для работы с потоками C# предоставляет класс Thread из пространства имен System.Threading. Классу Thread при создании нового объекта передается делегат ThreadStart или ParameterizedThreadStart, указывающий на метод, который будет запущен потоком. Отличаются эти два делегата тем, что ParameterizedThreadStart содержит параметр типа object для передачи данных в поток. Для запуска потока применяется метод Start класса Thread:

using System;
using System.Threading;

class Program {
    static void Main() {
        Thread thread = new Thread(SampleThreadMethod);
        thread.Start();

        for (int i = 0; i < 100; i++)
            Console.Write("1");
    }

    static void SampleThreadMethod() {
        for (int i = 0; i < 100; i++)
            Console.Write("0");
    }
}

Этот пример выводит в консоль последовательность нулей и единиц, которая на разных системах может отличаться. В нем для передачи метода потоку используется делегат ThreadStart без параметров. Но иногда нужно передать в поток какие то данные и в этом случае нужно использовать ParameterizedThreadStart для запуска потока с параметрами. ParameterizedThreadStart может принимать в качестве параметра один объект типа object. В следующем примере в поток передается символ “x” для вывода в консоль:

using System;
using System.Threading;

class Program {
    static void Main() {
        Thread thread = new Thread(new ParameterizedThreadStart(SampleThreadMethod));
        thread.Start("x"); // Передача параметра в поток

        for (int i = 0; i < 100; i++)
            Console.Write("1");
    }

    // Метод с параметром для запуска из потока
    static void SampleThreadMethod(object s) {
        for (int i = 0; i < 100; i++)
            Console.Write(s);
    }
}

Остановка потока

Для остановки потока используется метод Thread.Abort. Этот метод немедленно останавливает поток, при этом остановленный поток невозможно возобновить. В следующем примере используется метод Thread.Abort для завершения потока:

using System;
using System.Threading;

class Program {
    static void Main() {
        Thread thread = new Thread(SampleThreadMethod);
        thread.Start();

        for (int i = 0; i < 100; i++)
            Console.Write("1");

        // Остановка потока
        thread.Abort();

        Console.ReadKey();        
    }

    static void SampleThreadMethod() {
        for (int i = 0; i < 1000; i++)
            Console.Write("0");
    }
}

При вызове метода Thread.Abort в целевом потоке (тот к которому применяется этот метод) среда CLR генерирует исключение ThreadAbortException (если целевому потоку уже были переданы кванты времени, т.е. он начал выполняться). Целевой поток может обработать это исключение и выполнить в блоке catch произвольный код в ответ на остановку. Вот как выглядит предыдущий пример с перехватом исключения ThreadAbortException .

using System;
using System.Threading;

class Program {
    static void Main() {
        Thread thread = new Thread(SampleThreadMethod);
        thread.Start();

        for (int i = 0; i < 100; i++)
            Console.Write("1");

        thread.Abort();

        Console.ReadKey();        
    }

    static void SampleThreadMethod() {
        try {
            for (int i = 0; i < 1000; i++)
                Console.Write("0");
        }
        // Перехват исключения вызванного остановкой потока
        catch( ThreadAbortException ex ) {
            Console.WriteLine("Поток грубо остановлен");
        }
    }
}

Приостановка потока

Можно временно приостановить выполнение потока. Для этого используется статического метод Thread.Sleep(). Этот метод приостанавливает поток из которого он вызван на заданное время:

using System;
using System.Threading;

class Program {
    static void Main() {
        // Приостановка потока на 1000 мс
        Thread.Sleep(1000);

        for (int i = 0; i < 100; i++)
            Console.Write("1");

        Console.ReadKey();
    }
}

В этом примере в основном потоке программы вызывается метод Thread.Sleep(), в качестве параметра ему передается количество миллисекунд, в течение которого поток будет простаивать.

Есть две перегруженных версии метода Thread.Sleep():

public static void Sleep( TimeSpan timeout );
public static void Sleep( int millisecondsTimeout );

Первая версия метода принимает в качестве параметра структуру TimeSpan, определяющую промежуток времени для остановки, вторая в качестве параметра принимает целочисленное значение, определяющее количество миллисекунд для остановки потока.


Ожидание завершения потока

Дождаться завершения другого потока можно с помощью метода Thread.Join():

using System;
using System.Threading;

class Program {
    static void Main() {
        Thread thread = new Thread( SampleThreadMethod );
        thread.Start();

        // Ожидание завершения потока
        thread.Join();   
    }

    static void SampleThreadMethod() {        
        for (int i = 0; i < 1000; i++)
            Console.Write("0");
    }
}

В предыдущем примере для ожидания завершения созданного потока вызывается метод Thread.Join(). Если его не вызвать программа завершится раньше чем закончится выполнение вторичного потока.

Метод Thread.Join() имеет несколько перегрузок:

public void Join();
public bool Join(int millisecondsTimeout);
public bool Join(TimeSpan timeout);

В первом случае метод Join() вызывается без параметров, таким образом вызывающий поток ожидает завершения целевого потока неопределенное время. Это может привести к полному зависанию программы в случае если целевой поток вошел в бесконечный цикл или блокирован.

Следующие два варианта метода Join() в качестве параметров принимают либо количество миллисекунд, либо структуру TimeSpan, определяющую интервал времени в течение которого допускается ожидание завершения целевого потока.

avatar
5000
  Подписаться  
Уведомление о