КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Клас Thread
Підтримка багатопоточності здійснюється в.NET в основному за допомогою простору імен System.Threading. Деякі типи цього простору описані в таблиці 10.1
Таблиця 10.1. Деякі типи простору імен System.Threading
Продовження таблиці 10.1
Первинний потік створюється автоматично. Для запуску вторинних потоків використовується клас Thread. При створенні об'єкту-потоку йому передається делегат, що визначає метод, виконання якого виділяється в окремий потік:
Thread t = new Thread (new ThreadStart(ім'я_методу));
Після створення потоку заданий метод починає в ньому свою роботу, а первинний потік продовжує виконуватися. У лістингу 10.10 приведений приклад одночасної роботи двох потоків.
Лістинг 10.10. Створення вторинного потоку using System; using System.Threading; namespace ConsoleApplication1 { class Program { static public void Hedgehog() // метод для вторинного потоку { for (int i = 0; i < 6; ++i) { Console.WriteLine(i); Thread.Sleep(1000); } } static void Main() { Console.WriteLine("Первинний потік " + Thread.CurrentThread.GetHashCode()); Thread ta = new Thread(new ThreadStart(Hedgehog)); Console.WriteLine("Вторинний потік " + ta.GetHashCode()); ta.Start(); for (int i = 0; i > -6; --i) { Console.Write(" " + i); Thread.Sleep(400); } } } } Результат роботи програми: Первинний потік 1 Вторинний потік 3 -1 -21 -3 -42 -53
У лістингу використовується метод Sleep, що зупиняє функціонування потоку на задану кількість мілісекунд. Як бачите, обидва потоки працюють одночасно. Якби вони працювали з одним і тим же файлом, він був би зіпсований так само, як і приведене виведення на консоль, тому такий спосіб розпаралелювання обчислень має сенс тільки для роботи з різними ресурсами. У таблиці 10.2 перераховані основні елементи класу Thread.
Таблиця 10.2. Основні елементи класу Thread
Продовження таблиці 10.2
Можна створити декілька потоків, які спільно використовуватимуть один і той же код. Приклад приведений в лістингу 10.11.
Лістинг 10.11. Потоки, що використовують один об'єкт using System; using System.Threading;
namespace ConsoleApplication1 { class Classl { public void Do() { for (int i = 0; i < 4; ++i) { Console.Write(" " + i); Thread.Sleep(3); } } } class Program { static void Main() { Classl a = new Classl(); Thread tl = new Thread(new ThreadStart(a.Do)); tl.Name = "Second"; Console.WriteLine("Потік " + tl.Name); tl.Start(); Thread t2 = new Thread(new ThreadStart(a.Do)); t2.Name = "Third"; Console.WriteLine("Потік " + t2.Name); t2.Start(); } } }
Результат роботи програми: Потік Second Потік Third 0 0 1 1 2 2 3 3
Варіанти виведення можуть декілька розрізнятися, оскільки один потік перериває виконання іншого в невідомі моменти часу. Для того, щоб блок коду міг використовуватися в кожен момент тільки одним потоком, застосовується оператор lock. Формат оператора:
lock (вираз) блок_операторів
Вираз визначає об'єкт, який потрібно заблокувати. Для звичайних методів як вираз використовується ключове слово this. Для статичних - typeof (клас). Блок операторів задає критичну секцію коду, яку потрібно заблокувати. Наприклад, блокування операторів в приведеному раніше методі Do виглядає таким чином:
public void Do() { Lock(this) { for (int i = 0; i < 4; ++i) { Console.Write(" " + i); Thread.Sleep(30); } } } Результат роботи програми: Потік Second Потік Third 0 1 2 3 0 1 2 3
10.3.2. Асинхронні делегати Делегат можна викликати на виконання або синхронно, як у всіх приведених раніше прикладах, або асинхронний за допомогою методів BeginInvoke і EndInvoke. При виклику делегата за допомогою методу BeginInvoke середовище виконання створює для виконання методу окремий потік і повертає управління операторові, наступному за викликом. При цьому в початковому потоці можна продовжувати обчислення Якщо при виклику BeginInvoke був вказаний метод зворотного виклику, цей метод викликається після завершення потоку. Метод зворотного виклику також задається за допомогою делегата, при цьому використовується стандартний делегат AsyncCallback. У методі, зворотного виклику для набуття повертаємого значення і вихідних параметрів застосовується метод EndInvoke. Якщо метод зворотного виклику не був вказаний в параметрах методу BeginInvoke, метод EndInvoke можна використовувати в потоці, що ініціював запит. У лістингу 10.11 наводяться два приклади асинхронного виклику методу, що виконує розкладання числа на множники. Клас Factorizer містить метод Factorize, що виконує розкладання на множники. Цей метод асинхронно викликається двома способами: у методі Num1 метод зворотного виклику задається в BeginInvoke, в методі Num2 мають місце очікування завершення потоку і безпосередній виклик EndInvoke.
Лістинг 10.11. Асинхронні делегати using System; using System.Threading; using System.Runtime.Remoting.Messaging;
namespace AsynchroneDelegat { // асинхронний делегат public delegate bool AsyncDelegate(int Num, out int m1, out int m2);
// клас, що виконує розкладання числа на множники public class Factorizer { public bool Factorize(int Num, out int m1, out int m2) { m1 = 1; m2 = Num; for (int i = 2; i < Num; i++) if (0 == (Num % 1)) { m1 = i; m2 = Num / i; break; } if (1 == m1) return false; else return true; } } // клас, одержуючий делегати і результати public class PNum { private int Number; public PNum(int number) { Number = number; }
[OneWayAttribute()] // метод, одержуючий результати public void Res(IAsyncResult ar) { int m1, m2; // отримання делегата з IAsyncResult AsyncDelegate ad = (AsyncDelegate)((AsyncResult)ar).AsyncDelegate; // отримання результатів виконання методу Factorize ad.EndInvoke(out m1, out m2, ar); // виведення результатів Console.WriteLine("Перший спосіб: множетелі {0}: {1} {2}", Number, m1, m2); }
} // демонстраційний клас public class Simple { // спосіб 1: використовується функція зворотного виклику public void Num1() { Factorizer f = new Factorizer(); AsyncDelegate ad = new AsyncDelegate(f.Factorize); int Num = 1000589023, tmp; // створення екземпляра класу, який буде викликаний // після завершення роботи методу Factorize PNum n = new PNum(Num); // завдання делегата методу зворотного виклику AsyncCallback callback = new AsyncCallback(n.Res); // асинхронний виклик методу Factorize IAsyncResult ar = ad.BeginInvoke(Num, out tmp, out tmp, callback, null); // тут виконання якихось подальших дій } // спосіб 2: використовується очікування закінчення виконання public void Num2() { Factorizer f = new Factorizer(); AsyncDelegate ad = new AsyncDelegate(f.Factorize);
int Num = 1000589023, tmp;
// створення екземпляра класу, який буде викликаний // після завершення роботи методу Factorize PNum n = new PNum(Num); // завдання делегата методу зворотного виклику AsyncCallback callback = new AsyncCallback(n.Res); // асинхронний виклик методу Factorize IAsyncResult ar = ad.BeginInvoke(Num, out tmp, out tmp, null, null); // очікування завершення ar.AsyncWaitHandle.WaitOne(10000, false); if (ar.IsCompleted) { int m1, m2; // отримання результатів виконання методу Fractorize ad.EndInvoke(out m1, out m2, ar); // виведення результатів Console.WriteLine("Другий спосіб: множетелі {0}: {1} {2}", Num, m1, m2); } } } class Program { static void Main() { Simple s = new Simple(); s.Num1(); s.Num2(); }
} }
Результат роботи програми: Перший спосіб: множники 1000589023: 7 142941289 Другий спосіб: множники 1000589023: 7 142941289
Атрибут [ OneWayAttribute() ] позначає метод якщо не має повертаємого значення і вихідних параметрів.
10.4. Рекомендації по програмуванню
Делегати широко застосовуються в бібліотеці.NET як самостійно, так і для підтримки механізму подій. Делегат є особливим видом класу, що нагадує собою інтерфейс, але, на відміну від нього, задає тільки одну сигнатуру методу. У мові C ++ аналогом делегата є вказівка на функцію, але вона не володіє безпекою і зручністю використання делегата. Завдяки делегатам стає можливою гнучка організація взаємодії, що дозволяє підтримувати узгоджений стан взаємозв'язаних об'єктів. Основною метою створення багатопотокових застосувань є підвищення загальної продуктивності програми. Проте розробка багатопотокових застосувань складніша, оскільки при цьому виникають проблеми синхронізації даних, пов'язаних з можливістю доступу до одних і тих же даних з боку декількох потоків. РОЗДІЛ 11. РОБОТА З ФАЙЛАМИ
Файл - іменована інформація на зовнішньому носієві, наприклад на жорсткому або гнучкому магнітному диску. Логічно файл можна представити як кінцева кількість послідовних байтів, тому такі пристрої, як дисплей, клавіатура і принтер, також можна розглядати як файли. Передача даних із зовнішнього пристрою в оперативну пам'ять називається читанням, або введенням, зворотний процес - записом, або виведенням. Введення-виведення в С# виконується за допомогою підсистеми введення-виведення і класів бібліотеки. NET. У цьому розділі розглядається обмін даними з файлами і консоллю. Обмін даними реалізується за допомогою потоків. Потік (stream) - це абстрактне поняття, що відноситься до будь-якого перенесення даних від джерела до приймача. Потоки забезпечують надійну роботу як із стандартними, так і з визначеними користувачем типами даних. Потік визначається як послідовність байтів і не залежить від конкретного пристрою, з яким проводиться обмін (оперативна пам'ять, файл на диску, клавіатура або принтер). Обмін з потоком для підвищення швидкості передачі даних проводиться, як правило, через спеціальну область оперативної пам'яті - буфер. Буфер виділяється для кожного відкритого файлу. При записі у файл вся інформація спочатку прямує в буфер і там накопичується до тих пір, поки весь буфер не заповниться. Тільки після цього або після спеціальної команди скидання відбувається передача даних на зовнішній пристрій. При читанні з файлу дані спочатку прочитуються в буфер, причому не стільки, скільки запрошується, а скільки поміщається в буфер. Механізм буферізації дозволяє швидше і ефективно обмінюватися інформацією із зовнішніми пристроями. Для підтримки потоків бібліотека. NET містить ієрархію класів, основна частина якої представлена на рис. 11.1. Ці класи визначені в просторі імен System.IO. Окрім класів там описана велика кількість перелічень для завдання різних властивостей і режимів. Класи бібліотеки дозволяють працювати в різних режимах з файлами, каталогами і областями оперативної пам'яті. Короткий опис класів приведений в таблиці 11.1. Як можна бачити з таблиці, виконувати обмін із зовнішніми пристроями можна на рівні: - двійкового представлення даних (BinaryReader, BinaryWriter); - байтів (FileStream); - тексту, тобто символів (StreamWriter, StreamReader). У. NET використовується кодування Unicode, в якому кожен символ кодується двома байтами. Класи, що працюють з текстом, є оболонками класів, що використовують байти, і автоматично виконують те, що кодується з байтів в символи і назад.
Рис. 11.1. Класи бібліотеки. NET для роботи з потоками
Двійкові і байтові потоки зберігають дані в тому ж вигляді, в якому вони представлені в оперативній пам'яті, тобто при обміні з файлом відбувається побітове копіювання інформації. Двійкові файли застосовуються не для перегляду їх людиною, а для використання в програмах. Доступ до файлів може бути послідовним, коли черговий елемент можна прочитати (записати) тільки після аналогічної операції з попереднім елементом, і довільним, або прямим, при якому виконується читання (запис) довільного елементу за заданою адресою. Текстові файли дозволяють виконувати тільки послідовний доступ, в двійкових і байтових потоках можна використовувати обидва методи. Прямий доступ у поєднанні з відсутністю перетворень забезпечує високу швидкість отримання потрібної інформації. Методи форматованого введення, за допомогою яких можна виконувати введення з клавіатури або з текстового файлу значень арифметичних типів, в С# не підтримуються. Для перетворення з символьного в числове представлення використовуються методи класу Convert або метод Parse, розглянуті в попередніх розділах.
Таблиця 11.1
Основні класи простору імен System.IO
Форматоване виведення, тобто перетворення з внутрішньої форми представлення числа в символьну, зрозумілу людині, виконується за допомогою перевантажених методів ToString. Окрім перерахованих класів в бібліотеці.NET є класи XmlTextReader і XmlTextWriter, призначені для формування і читання коду у форматі XML. Розглянемо прості способи роботи з файловими потоками. Використання класів файлових потоків в програмі припускає наступні операції: 1. Створення потоку і зв’язування його з фізичним файлом. 2. Обмін (уведення-виведення). 3. Закриття файлу. Кожен клас файлових потоків містить декілька варіантів конструкторів, за допомогою яких можна створювати об'єкти цих класів різними способами і в різних режимах. Наприклад, файли можна відкривати тільки для читання, тільки для запису або для читання і запису. Ці режими доступу до файлу містяться в переліченні FileAccess, визначеному в просторі імен System. IO. Константи перелічення приведені в таблиці 11.2.
Таблиця 11.2 Значення FileAccess
Можливі режими відкриття файлу визначені в переліченні FileMоdе (таблиця. 11.3).
Таблиця 11.3 Значення FilеМоdе
Режим FileMode.Append можна використовувати тільки спільно з доступом типу FileAccess.Write, тобто для файлів, що відкриваються для запису. Режими сумісного використання файлу різними користувачами визначає перелічення FileShare (таблиця 11.4). Таблиця 11.4 Значення FileShare
11.1. Потоки байтів
Уведення-виведення у файл на рівні байтів виконується за допомогою класу FileStream, який є спадкоємцем абстрактного класу Stream, що визначає набір стандартних операцій з потоками. Елементи класу Stream описані в таблиці 11.5.
Таблиця 11.5 Елементи класу Stream
Клас FileStream реалізує ці елементи для роботи з дисковими файлами. Для визначення режимів роботи з файлом використовуються стандартні перелічення FileMode, FileAccess і FileShare. Значення цих перелічень приведені в таблицях 11.2-11.4. У лістингу 11.1 представлений приклад роботи з файлом. У прикладі демонструються читання і запис одного байта і масиву байтів, а також позиціонування в потоці.
Лістинг 11.1. Приклад використання потоку байтів using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text;
namespace examp74 { class Class1 { static void Main() { FileStream f = new FileStream("test.txt", FileMode.Create, FileAccess.ReadWrite); f.WriteByte(100); // у початок файлу записується число 100 byte[] х = new byte[10]; for (byte i = 0; i < 10; ++i) { х[i] = (byte)(10 - i); f.WriteByte(i); // записується 10 чисел від 0 до 9 } f.Write(х, 0, 5); // записується 5 елементів масиву byte[] у = new byte[20]; f.Seek(0, SeekOrigin.Begin); // поточний покажчик - на початок f.Read(у, 0, 20); // читання з файлу в масив foreach (byte elem in у) Console.Write(" " + elem); Console.WriteLine(); f.Seek(5, SeekOrigin.Begin); // поточний покажчик - на 5-й елемент int а = f.ReadByte(); // читання 5-го елементу Console.WriteLine(а); а = f.ReadByte(); // читання 6-го елементу Console.WriteLine(а); Console.WriteLine(" Поточна позиція в потоці " + f.Position); f.Close(); } } }
Результат роботи програми: 100 01234 56789 10 98760000 Поточна позиція в потоці 7 Поточна позиція в потоці спочатку встановлюється на початок файлу (для будь-якого режиму відкриття, окрім Append) і зрушується на одну позицію при записі кожного байта. Для установки бажаної позиції читання використовується метод Seek, що має два параметри: перший задає зсув в байтах щодо точки відліку, що задається другим. Точки відліку задаються константами перелічення SeekOrigin: початок файлу - Begin, поточна позиція - Current і кінець файлу - End. У даному прикладі файл створювався в поточному каталозі. Можна вказати і повний шлях до файлу, наприклад:
FileStream f = new FileStream(@"D:\C#\test.txt", FileMode.Create, FileAccess.ReadWrite);
Операції по відкриттю файлів можуть завершитися невдало, наприклад, при помилці в імені існуючого файлу або за відсутності вільного місця на диску, тому рекомендується завжди контролювати результати цих операцій. У разі непередбачених ситуацій середовище виконання генерує різні виключення, обробку яких слід передбачити в програмі, наприклад: · FileNotFoundException, якщо файлу у вказаному каталозі не існує; · DirectoryNotFoundException, якщо не існує вказаний каталог; · ArgumentException, якщо невірно заданий режим відкриття файлу; · IOException, якщо файл не відкривається із-за помилок введення-виведення. Можливі і інші виняткові ситуації. Зручно обробляти найбільш вірогідні помилки роздільно, щоб надати користувачеві програми в повідомленні, що виводиться, найбільш точну інформацію. У приведеному далі прикладі окремо перехоплюється помилка в імені файлу, а потім обробляється решта всіх можливих помилок:
try { FileStream f = new FileStream(@"d:\C#\test.tx", FileMode.Open, FileAccess.Read); … // дії з файлом f.Close(); } catch(FileNotFoundException e) { Console.WriteLine(е.Message); Console.WriteLine(" Перевірте правильність імені файлу!"); return; } catch(Exception e) { Console.WriteLine("Error: " + e.Message); return; } При закритті файлу звільняються всі пов'язані з ним ресурси, наприклад, для файлу, відкритого для запису, у файл вивантажується вміст буфера. Тому рекомендується завжди закривати файли після закінчення роботи, особливо файли, відкриті для запису. Якщо буфер потрібно вивантажити, не закриваючи файл, використовується метод Flush.
Дата добавления: 2014-12-27; Просмотров: 1131; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |