Пример создания потока с использованием параметра

Лекция(КПО) №4

Разработка многопоточных приложений

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

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

 

Проектирование потоков (UML)

 

Средства .Net для обработки потоков.

System.Threading -пространство имен. Это пространство имен содержит в себе классы поддерживающие многопоточное программирование.

Класс Thread, который мы используем далее в коде.

Thread myThread = new Thread(func);

Start() -

Thread.Sleep(n) -

Пример кода.

using System;

using System.Threading;

namespace ConsoleApplication1

{

class Program

{

static void Main(string[] args)

{

Thread myThread = new Thread(func);

myThread.Start(); //запускаем поток

for (int i = 0; i < 10; i++ )

{

Console.WriteLine("Поток 1 выводит " + i);

Thread.Sleep(1);

}

Console.Read(); //Приостановим основной поток

}

 

static void func()

{

for (int i = 0; i < 10; i++)

{

Console.WriteLine("Поток 2 выводит " + i.ToString());

Thread.Sleep(1);

}

В результате ваша программа выведет в консоль примерно следующее:
Поток 1 выводит 0
Поток 2 выводит 0
Поток 1 выводит 1
Поток 1 выводит 2
Поток 1 выводит 3
Поток 2 выводит 1
<…> и т.д.

Потоки бывают двух видов: приоритетные и фоновые.

Фоновые потоки автоматически завершаются при завершении приоритетных. По умолчанию в приоритетном потоке запускается функция Main() а остальные потоки создаются фоновыми.

Для того чтобы не допустить что бы главный поток завершился до окончания производных необходимо использовать поле IsAlive, которое возвращает true если поток активен. Либо метод Join(), который заставляет поток в котором он вызван ожидать завершения работы потока которому он принадлежит.

Изменение типа потока. Свойство потока IsBackground определяет является ли поток фоновым. Таким образом, мы можем сделать поток приоритетным. myThread.IsBackground = false;

Пример создания потока с использованием параметра

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

using System;

using System.Threading;

 

namespace ConsoleApplication1

{

class Program

{

class myThread

{

Thread thread;

public myThread(string name, int num)

//Конструктор получает имя функции и номер до кторого ведется счет

{

thread = new Thread(this.func);

thread.Name = name;

thread.Start(num);//передача параметра в поток

}

 

void func(object num)//Функция потока, передаем параметр

{

for (int i = 0;i < (int)num;i++ )

{

Console.WriteLine(Thread.CurrentThread.Name + " выводит " + i);

Thread.Sleep(1);

}

Console.WriteLine(Thread.CurrentThread.Name + " завершился");

}

 

 

}

static void Main(string[] args)

{

myThread t1 = new myThread("Thread 1", 6);

myThread t2 = new myThread("Thread 2", 3);

myThread t3 = new myThread("Thread 3", 2);

Console.Read();

}

}

}

Пример использования метода Join()

static int[] array;static void Main(string[] args){ Thread filler = new Thread(FillArray); filler.Start(); // Пока массив заполняется, можно заняться своими делами. for (int i = 1; i < 4; i++) { Console.WriteLine("Море волнуется {0}", i); Thread.Sleep(1000); } // Свои дела завершили, дальше надо вывести в консоль массив, но делать это нет смысла, пока он не заполнится. // Потому ждем завершения потока, который заполняет массив. filler.Join(); Console.WriteLine("Заполненный массив: {0}", string.Join(" ", array)); Console.ReadKey();}// Неторопливо заполняет массив случайными числамиstatic void FillArray(){ Random rand = new Random(); array = new int[20]; for (int i = 0; i < array.Length; i++) { array[i] = rand.Next(100); Thread.Sleep(200); }}

 

Invoke- это немного другое. Он позволяет перенаправить выполнение определенных инструкций в поток, которому принадлежит объект, на котором он вызывается.

Наиболее частое использование - обновление контролов интерфейса из потока, в котором они не созданы. Без использования Invoke при попытке получить доступ к контролам, вылетит исключение (да-да, выброс можно отключить, но это не рекомендуется).

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

public Form1(){ InitializeComponent(); //Создание и запуск отдельного потока, в котором будет //обновляться прогресс var th = new Thread(DoSomething); th.Start();} void DoSomething(){ for (int i = 0; i <= 100; i++) { if (this.InvokeRequired) // Метод DoSomething вызывается в отдельном потоке, надо бы //перенаправить обновление в основной {// Перенаправление выполнения метода UpdateProgress в основной //поток, иначе вылетит исключение this.Invoke(new Action(UpdateProgress)); } else // Метод DoSomething вызывается в родительском потоке, //перенаправление не нужно UpdateProgress(); Thread.Sleep(50); }} void UpdateProgress(){ // Увиличить значение прогресса на 1 progressBar1.PerformStep();}