Вопрос по Курсу: "Параллельное программирование с использованием MS VisualStudia 2010". При компиляции Самостоятельного задания (одновременная отрисовка прямоугольников, эллипсов и выдача в текст-бокс случайного числа) среда предупреждает: suspend - устаревшая команда; примените monitor, mutex и т.п. Создаётся впечатление, что Задание создано в более поздней среде, чем VS 2010. |
Томский политехнический университет
Опубликован: 23.01.2013 | Доступ: свободный | Студентов: 1157 / 192 | Длительность: 12:09:00
Тема: Программирование
Специальности: Программист, Архитектор программного обеспечения
Теги:
Самостоятельная работа 4:
Создание приложения с использованием PLINQ
< Лекция 9 || Самостоятельная работа 4 || Лекция 10 >
Аннотация: В рамках данного практического занятия, будет создано консольное приложение, которое получает из СУБД MS SQL данные по сотрудникам и оформленными ими заказами (стоимость, количество, наименования продукта) с помощью LINQ и PLINQ запросов.
- Создадим консольное приложение и назовем его, к примеру, "PLINQApplicaions":
- Теперь необходимо установить связь с базой данных. Для этого откроем обозреватель серверов, с помощью пункта Server Explorer меню View или с помощью сочетания горячих клавиш "Ctrl + Alt+S"
- Далее создадим соединение к серверу. Для этого щелкните правой кнопкой мыши по "Data Connections" и выберите пункт "Add connection…"
- Появится диалоговое окно "Add Connection". В диалоговом окне необходимо ввести адрес или имя сервера (Sever name), где расположена база данных, тип доступа к базе данных (SQL аутентификация или Windows аутентификация) и выбрать из писка нужную базу данных (в нашем случае база данных Northwind):
Общая схема данных Northwind выглядит следующим образом:
- Если связь с базой успешно установится, то в обозревателе серверов появится база данных Northwind:
- Теперь необходимо создать связку между SQL и LINQ. Для этого в обозреватели проекта (Solution Explorer), для этого щелкните правой кнопкой мыши по названию проекта и выберите пункт "New Item…" или с помощью клавиш "Ctrl+Shift+A":
- В появившемся диалоговом окне выберите пункт "Linq to SQL Classes", и введите название добавляемого файла (в нашем случае NorthwindDatabase):
- Появится вкладка NorthwindDatabase.dbml. Теперь из обозревателя серверов (Server Explorer) перетащите, с помощью мыши, нужные таблицы в окно созданного файла:
- Теперь, перейдем в файл Program.cs и создадим в классе Program новый объект класса NorthwindDataBaseDataContext (где NorthwindDataBase - название файла созданного ранее NorthwindDatabase.dbml):
class Program { private static NorthwindDataBaseDataContext db = new NorthwindDataBaseDataContext(); …. }
- Создадим класс InfoEmployees, со следующими свойствами:
public class InfoEmployees { public string FirstName { get; set; } public string LastName { get; set; } public string ProductName { get; set; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public DateTime OrderDate { get; set; } }
- Создадим метод, возвращающий список объектов типа InfoEmployees, с использованием интерфейса IEnumerable и LINQ - запроса к базе данных:
public static IEnumerable<InfoEmployees> GetInfoEmployees() { var emp = from em in db.Employees join o in db.Orders on em.EmployeeID equals o.EmployeeID join orderdetails in db.Order_Details on o.OrderID equals orderdetails.OrderID join product in db.Products on orderdetails.ProductID equals product.ProductID select new InfoEmployees() { FirstName = em.FirstName, LastName = em.LastName, ProductName = product.ProductName, UnitPrice=orderdetails.UnitPrice, Quantity=orderdetails.Quantity, OrderDate=o.OrderDate.Value }; return emp; }
- В метод Main() добавим код, который выводит список сотрудников с помощью LINQ-запроса:
static void Main(string[] args) { IEnumerable<InfoEmployees> em = GetInfoEmployees(); int count = 1; var infoemp = from e in em select e; foreach (var item in infoemp) { Console.WriteLine("{0} {1} {2} {3} {4} {5}", item.FirstName, item.LastName, item.ProductName, item.Quantity, item.UnitPrice.ToString("C", CultureInfo.CreateSpecificCulture("en-US")), item.OrderDate.ToString("dd.MM.yyyy")); count++; } Console.WriteLine("--------------------- "); Console.WriteLine("Количество записей {0}", count); Console.ReadLine(); }
- Запустим приложение в результате, на экран выведется следующее:
- Теперь, модифицируем программу. Создадим отдельный перегруженный метод ProcessItem() для вывода сотрудников на экран, и "заморозим" выполнение данного метода, при каждой итерации, на 10 миллисекунд (Thread.Sleep(10)) для наглядности производительности распараллеленного запроса:
public static string ProcessItem(InfoEmployees item) { Thread.Sleep(10); return string.Format ("{0} {1} {2} {3} {4} {5}", item.FirstName, item.LastName, item.ProductName, item.Quantity, item.UnitPrice.ToString("C", CultureInfo.CreateSpecificCulture("en-US")), item.OrderDate.ToString("dd.MM.yyyy")); }
- Модифицируем метод Main(). В select запросе вызовем метод ProcessItem() и передадим в него объект класса InfoEmployees:
static void Main(string[] args) { IEnumerable<InfoEmployees> em = GetInfoEmployees(); int count = 1; var infoemp = from e in em select ProcessItem(e); foreach (var item in infoemp) { Console.WriteLine(item); count++; } Console.WriteLine("--------------------- "); Console.WriteLine("Количество записей {0}", count); Console.ReadLine(); }
- Запустим программу. В результате программа выведет на экран следующее, но с гораздо большей задержкой (из-за использования метода Thread.Sleep()):
- Для определения производительности LINQ - запроса модифицируем код в методе Main():
static void Main(string[] args) { System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); IEnumerable<InfoEmployees> em = GetInfoEmployees(); int count = 1; var infoemp = from e in em select ProcessItem(e); foreach (var item in infoemp) { Console.WriteLine(item); count++; } long elapsed = sw.ElapsedMilliseconds; Console.WriteLine("--------------------- "); Console.WriteLine("Количество записей {0}", count); Console.WriteLine("Общее время выполнения запроса {0} мс", elapsed); Console.ReadLine(); }
Примечание: Для более точно расчета времени выполнения запроса можно использовать sw.ElapsedTicks
- Запустим программу. Программа выведет на экран, кроме результата LINQ-запроса, еще и время выполнения запроса (в нашем случае запрос выполняется 22409 мс):
- Изменим программу в методе Main(), для того что бы распараллелить выполнение метода ProcessItem():
Примечание: Как видно из примера кода - PLINQ - запрос отличается от LINQ - запроса, только вызовом метода AsParallel() в фрагменте: em.AsParallel().AsOrdered(). Метод AsOrdered() предназначен для упорядочивания записей независимо от наличия упорядоченности по умолчанию. Также можно использовать следующий формат записи LINQ - запроса: var infoemp = em.AsParallel().Select(ProcessItem).AsOrdered();
static void Main(string[] args) { System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); IEnumerable<InfoEmployees> em = GetInfoEmployees(); int count = 1; var infoemp = from e in em.AsParallel().AsOrdered() select ProcessItem(e); foreach (var item in infoemp) { Console.WriteLine(item); count++; } long elapsed = sw.ElapsedMilliseconds; Console.WriteLine("--------------------- "); Console.WriteLine("Количество записей {0}", count); Console.WriteLine("Общее время выполнения запроса {0} мс", elapsed); Console.ReadLine(); }
- Запустим программу. И как видно из результата выполнения, общее время выполнения PLINQ -запроса, сократилось примерно вдвое и составило 11938 мс по сравнению с предыдущим результатом 22409 мс:
Листинг кода программы:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Globalization; namespace PLINQApplications { class Program { private static NorthwindDataBaseDataContext db = new NorthwindDataBaseDataContext(); public class InfoEmployees { public string FirstName { get; set; } public string LastName { get; set; } public string ProductName { get; set; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public DateTime OrderDate { get; set; } } public static IEnumerable<InfoEmployees> GetInfoEmployees() { var emp = from em in db.Employees join o in db.Orders on em.EmployeeID equals o.EmployeeID join orderdetails in db.Order_Details on o.OrderID equals orderdetails.OrderID join product in db.Products on orderdetails.ProductID equals product.ProductID select new InfoEmployees() { FirstName = em.FirstName, LastName = em.LastName, ProductName = product.ProductName, UnitPrice=orderdetails.UnitPrice, Quantity=orderdetails.Quantity, OrderDate=o.OrderDate.Value }; return emp; } public static string ProcessItem(InfoEmployees item) { Thread.Sleep(10); return string.Format ("{0} {1} {2} {3} {4} {5}", item.FirstName, item.LastName, item.ProductName, item.Quantity, item.UnitPrice.ToString("C", CultureInfo.CreateSpecificCulture("en-US")), item.OrderDate.ToString("dd.MM.yyyy")); } static void Main(string[] args) { System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); IEnumerable<InfoEmployees> em = GetInfoEmployees(); ); int count = 1; var infoemp = em.AsParallel().Select(ProcessItem).AsOrdered(); foreach (var item in infoemp) { Console.WriteLine(item); count++; } long elapsed = sw.ElapsedMilliseconds; Console.WriteLine("--------------------- "); Console.WriteLine("Количество записей {0}", count); Console.WriteLine("Общее время выполнения запроса {0} мс", elapsed); Console.ReadLine(); } } }
< Лекция 9 || Самостоятельная работа 4 || Лекция 10 >