Post on 18-Jul-2015
Толстиков Никита
tolstikov.n.s@gmail.com
Многопоточность
10.04.2015 1Потоки
План лекции
• Введение в многопоточность
• Управление потоками
10.04.2015 Толстиков Никита 2Потоки
Многопоточность
• C# поддерживает параллельное
исполнение на уровне потоков:
10.04.2015 Толстиков Никита 3LINQ
public static void Example(){
Thread t = new Thread(WriteY); // Создаем новый потокt.Start(); // запускаем WriteY()// Паралельно делаем что-то на основном потоке.for (int i = 0; i < 1000; i++) Console.Write("x");
}static void WriteY(){
for (int i = 0; i < 1000; i++) Console.Write("y");}
//Получится что-то такое://xxxxyyyyyyxxyyyxyxyyxyxyxyyxyyyyyyyyyyyxxxxxxxxxxyyyyyyyyyyyxxxxxxxx...
Многопоточность
• Потоки выполняются последовательно на одном ядре и параллельно на нескольких
• Потоки выполняются по расписанию составленному ОС
• Минимальное время выполнения - квант
• Каждый поток имеет собственный стек и контекст, но остальная память общая
10.04.2015 Толстиков Никита 4Потоки
Многопоточность
10.04.2015 Толстиков Никита 5Потоки
class TestClass{
private bool isPrinted = false;
public void Print(){
if (!isPrinted){
Thread.Yield(); //Выйти из выполнения потокаisPrinted = true;Console.WriteLine("Printed");
}}
}
Многопоточность
• Каждый поток имеет ссылку на свой тестовый класс
• Вывод детерминирован
10.04.2015 Толстиков Никита 6Потоки
public static void ExampleOwn(){
var testClass1 = new TestClass();var testClass2 = new TestClass();Thread t = new Thread(testClass1.Print);t.Start();
testClass2.Print();
//Result://Printed//Printed
}
Многопоточность
• Оба потока имеют ссылку на один и тот
же класс
• Вывод не детерминирован
10.04.2015 Толстиков Никита 7Потоки
public static void ExampleShare(){
var testClass1 = new TestClass();Thread t = new Thread(testClass1.Print);t.Start();
testClass1.Print();
//Result://Printed//Printed (undefined behavior)
}
Создание и запуск
• Поток инкапсулирован в классе Thread из
пространства имен System.Threading
• Для создания потока в него нужно
передать объект типа:
• Для запуска потока на исполнение нужно
вызвать метод Start
10.04.2015 Толстиков Никита 8Потоки
public delegate void ThreadStart();
Выполнение с аргументами
• Первый способ:
10.04.2015 Толстиков Никита 9Потоки
static void Example(){
Thread t = new Thread(() => Print("Hello from t!"));t.Start();
}
static void Print(string message){
Console.WriteLine(message);}
Выполнение с аргументами
• Примечание:
– Контекст может меняться:
10.04.2015 Толстиков Никита 10Потоки
string text = "text1";Thread t1 = new Thread(() => Console.WriteLine(text));
text = "text2";Thread t2 = new Thread(() => Console.WriteLine(text));
t1.Start();t2.Start();
//Result://text2//text2
Выполнение с аргументами
• Примечание:
– Контекст может меняться:
– FIX:
10.04.2015 Толстиков Никита 11Потоки
for (int i = 0; i < 10; i++){
new Thread(() => Console.Write(i)).Start();}//Result://0223557799
for (int i = 0; i < 10; i++){
int i1 = i;new Thread(() => Console.Write(i1)).Start();
}//Result://0123456789
Выполнение с аргументами
• Второй способ:
– Конструктор принимает также объект типа
10.04.2015 Толстиков Никита 12Потоки
static void Example(){
Thread t = new Thread(Print);t.Start("Hello from t!");
}
static void Print(object message){
string stringMessage = (string) message; //Нужен кастConsole.WriteLine(stringMessage);
}
public delegate void ParameterizedThreadStart(object obj);
Join
• Метод Join – дожидается окончания
выполнения потока
• Во время исполнения Join поток
находится в заблокированном состоянии и
не потребляет системных ресурсов
10.04.2015 Толстиков Никита 13Потоки
public void Join() ...public bool Join(int millisecondsTimeout)...public bool Join(TimeSpan timeout)...
Sleep
• Метод Sleep – ставит выполнения потока на паузу
• Метод Thread.Yield – аналогичен Thread.Sleep(0)
• Метод Thread.Yield можно поставить в любое место программы и она не должна сломаться
10.04.2015 Толстиков Никита 14Потоки
public bool Sleep(int millisecondsTimeout)...public bool Sleep(TimeSpan timeout)...
Именованные потоки
• Потоки имеют свойство Name
• Оно крайне полезно при отладке в
VisualStudio
10.04.2015 Толстиков Никита 15Потоки
public static void Example(){
Thread.CurrentThread.Name = "main";Thread worker = new Thread(Go);worker.Name = "worker";worker.Start();Go();
}static void Go(){
Console.WriteLine("Hello from " + Thread.CurrentThread.Name);}
Foreground и Background потоки
• Программа исполняется пока исполняется
хоть один foregraund поток
• По умолчанию все потоки foregraund
• Для изменения типа потока используется
свойство IsBackground
10.04.2015 Толстиков Никита 16Потоки
Foreground и Background потоки
• Если количество аргументов больше нуля, то программа не будет ждать ввода клавиши и сразу завершиться
• При завершении в таком стиле finally блоки в background потоках не выполняться
– Использовать Join
– Использовать пул-потоков
10.04.2015 Толстиков Никита 17Потоки
static void Main(string[] args){
Thread worker = new Thread(() => Console.ReadLine());if (args.Length > 0) worker.IsBackground = true;worker.Start();
}
Обработка исключений
• Любые try/catch/finally блоки
нерелевантны точке старта потока:
10.04.2015 Толстиков Никита 18Потоки
public static void Example(){
try{
new Thread(Go).Start();}catch (Exception ex){
// We'll never get here!Console.WriteLine("Exception!");
}}
static void Go() { throw null; }
Обработка исключений
• Контроль исключений должен
осуществляться в корневом методе потока:
10.04.2015 Толстиков Никита 19Потоки
public static void Example(){
new Thread(Go).Start();}static void Go(){
try{
throw null; // NullReferenceException будет поймано ниже}catch (Exception ex){
// Что-то залогировать, или посигналить другому потоку и закончить //исполнение
}}
Пул потоков
• Создает потоки заранее
• Позволяет пере использовать потоки
• Уменьшает оверхед при частом создании потоков
• По умолчанию потоки являются background
• Удобно использовать для «быстрых задач» в несколько квантов
• Для долгих (порядка секунд) лучше создавать потоки вручную
10.04.2015 Толстиков Никита 20Потоки
Пул потоков
10.04.2015 Толстиков Никита 21Потоки
public static void Example(){
ThreadPool.QueueUserWorkItem(Go);ThreadPool.QueueUserWorkItem(Go, 123);Console.ReadLine();
}
static void Go(object data) // data will be null with the //first call.
{Console.WriteLine("Hello from the thread pool! " + data);
}
//Results://Hello from the thread pool!//Hello from the thread pool! 123
• Как обычные, но вызываются асинхронно
на потоках из пула
Асинхронные делегаты
10.04.2015 Толстиков Никита 22Потоки
public static void Example(){
Func<string, int> method = Work;IAsyncResult cookie = method.BeginInvoke("test", null, null);//// ... here's where we can do other work in parallel...//int result = method.EndInvoke(cookie);Console.WriteLine("String length is: " + result);
}
static int Work(string s) { return s.Length; }
• BeginInvoke запускает метод на исполнение и возвращает IAsincResult«талончик» на получение результата
Асинхронные делегаты
10.04.2015 Толстиков Никита 23Потоки
public static void Example(){
Func<string, int> method = Work;IAsyncResult cookie = method.BeginInvoke("test", null, null);//// ... here's where we can do other work in parallel...//int result = method.EndInvoke(cookie);Console.WriteLine("String length is: " + result);
}
static int Work(string s) { return s.Length; }
• EndInvoke дожидается исполнения метода по «талончику» и возвращает результат
• Так же перебрасывает необработанные исключения
Асинхронные делегаты
10.04.2015 Толстиков Никита 24Потоки
public static void Example(){
Func<string, int> method = Work;IAsyncResult cookie = method.BeginInvoke("test", null, null);//// ... here's where we can do other work in parallel...//int result = method.EndInvoke(cookie);Console.WriteLine("String length is: " + result);
}
static int Work(string s) { return s.Length; }
• В BeginInvoke можно передать callback, который вызовется при завершении метода и объект как параметр
Асинхронные делегаты
10.04.2015 Толстиков Никита 25Потоки
public static void Example(){
Func<string, int> method = Work;method.BeginInvoke("test", Done, method);// ...//
}
static int Work(string s) { return s.Length; }
static void Done(IAsyncResult cookie){
var target = (Func<string, int>)cookie.AsyncState;int result = target.EndInvoke(cookie);Console.WriteLine("String length is: " + result);
}
The End
10.04.2015 Толстиков Никита 26LINQ