FaceFinance (Учет личных финансов)

Ваши деньги находятся без контроля?
Начните вести ее прямо сейчас и обретете контроль над вашими денежными средствами раз и навсегда.

Accounting of food (Учет продуктов питания)

Не можете правильно и быстро рассчитать необходимое количество продуктов?
Наша программа поможет вам в этом.

Work with clients (Работа с клиентами)

Не можете организовать работу с клиентами?
Наша программа является простой, удобной и функциональной CRM-системой.




МНОГОПОТОЧНЫЕ ПРИЛОЖЕНИЯ

Проход по ссылкам навигацииПомощь Многопоточные приложения

Что такое многопоточные приложения?

Если описать доступным языком - это приложения с несколькими «рабочими», которые одновременно выполняют разные или однотипные задачи. Зачем это нужно? Ресурсы компьютера используются не всегда эффективно. Например, ваша программа скачивает страницу из интернета, потом анализирует ее, затем - качает следующую. Во время анализа простаивает интернет соединение, а во время закачки - скучает процессор. Это можно исправить. Уже во время анализа текущей страницы параллельно качать следующую.

Я попытаюсь объяснить, как оптимизировать свои программы, используя многопоточность, и приведу пару примеров с кодом.

Для начала, давайте разберемся, когда можно распараллелить программу. Например, у вас есть один поток данных. Его нужно обрабатывать в строго определенном порядке и без результатов предыдущей обработки, следующую операцию выполнять нельзя. В такой программе можно создать дополнительные потоки, но будут ли они нужны? Мой преподаватель по компьютерным системам приводил следующий пример.

Допустим, у нас есть 2 рабочих, которые хорошо копают ямы. Предположим, что один выкопает яму глубиной 2 метра, за 1 час. Тогда два рабочих, выкопают эту яму за полчаса. Это похоже на правду. Давайте возьмем 3600 таких рабочих. Теоретически, они выкопают яму глубиной 2 метра за 1 секунду. Но на практике они будут друг другу мешать, топтаться на одном месте и нервничать.

Я надеюсь, вы поняли, что многопоточность нужна не всегда, и что утверждение «чем больше потоков - тем лучше» ошибочно. Потоки не должны мешать друг другу и должны использовать эффективно ресурсы системы, в которой они работают.

Далее немного практики. Что бы начать работать с потоками, необходимо подключить пространство имен System.Threading, добавив в начало файла с кодом следующую директиву:

using System.Threading;

Любой поток в C# это функция. Функции не могут быть сами по себе, они обязательно являются методами класса. Поэтому, что бы создать отдельный поток, нам понадобится класс с необходимым методом. Самый простой вариант метода возвращает void и не принимает аргументов:

void MyThreadFunction() { ... }

Пример запуска такого потока:

Thread thr = new Thread(MyThreadFunction);

thr.Start();

После вызова метода Start() у объекта потока, управление вернется сразу, но в этот момент уже начнет работать ваш новый поток. Новый поток выполнит тело функции MyThreadFunction и завершится. Мой друг спросил меня, а почему функция не возвращает значение? А потому, что его некуда вернуть. После вызова Start(), управление передается дальше, при этом созданный поток может работать еще длительное время. Что бы обмениваться данными между потоками, можно пользоваться переменными класса. Об этом позже.

Кроме того, существует еще один вариант метода, из которого можно сделать поток.

Выглядит он вот так:

void ThreadFunction(Object input) { ... }

Его назначение - применение в тех случаях, когда нужно при создании потоков, передавать им какие-то данные. Любой класс в C# наследуется от Object, поэтому можно передавать внутрь потока любой объект. Подробнее позже.

Давайте напишем простой пример, что бы проиллюстрировать работу потоков. Реализуем следующее. Наша программа запустит дополнительный поток, после чего выведет три раза на экран «Это главный поток программы!», в это время созданный поток выведет три раза на экран «Это дочерний поток программы!».

Вот, что получилось:

using System;

using System.Threading;

namespace ThreadsExample

{

class Program

{

static void Main(string[] args)

{

//Создаем объект потока

Thread thread = new Thread(ThreadFunction);

//Запускаем поток

thread.Start();

//Просто выводим 3 раза на экран заданный текст

int count = 3;

while(count 0)

{

Console.WriteLine("Это главный поток программы!");

--count;

}

//Ждем ввода от пользователя, что бы окно консоли не закрылось автоматически

Console.Read();

}

//Функция потока

static void ThreadFunction()

{

//Аналогично главному потоку выводим три раза текст

int count = 3;

while(count 0)

{

Console.WriteLine("Это дочерний поток программы!");

--count;

}

}

}

}

Ничего сложно. Хочу обратить ваше внимание, что у метода потока присутствует модификатор static, это нужно для того, что бы к ней можно было напрямую обратиться из главного потока приложения. Теперь запустите программу несколько раз и сравните результаты вывода в консоль. Обычно, порядок вывода сообщений в консоль при каждом запуске разный. Планировщик задач операционной системы по-разному распределяет процессорное время, поэтому порядок вывода разный. Еще одно полезное свойство потоков заключается в том, что вам не нужно беспокоиться о правильности распараллеливания их на процессоре, планировщик задач все сделает сам.

Что бы создать несколько потоков, необязательно использовать несколько функций, если потоки одинаковые.

Вот пример:

using System;

using System.Threading;

namespace SomeThreadsExample

{

class Program

{

static void Main(string[] args)

{

//Создаем в цикле 10 потоков

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

{

Thread thread = new Thread(ThreadFunction);

thread.Start();

}

Console.WriteLine("Создание потоков завершено!");

Console.Read();

}

static void ThreadFunction()

{

//Просто выводим что-нибудь в консоль для наглядности

Console.WriteLine("Я поток!");

}

}

}

Как видите, для создания 10 потоков нам понадобилась всего 1 функция.

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

Вот код:

using System;

using System.Threading;

namespace SomeThreadsExample

{

class Program

{

static void Main(string[] args)

{

//Создаем поток

Thread thread = new Thread(ThreadFunction);

thread.Start();

//Вызываем этот же метод без создания потока

ThreadFunction();

Console.Read();

}

static void ThreadFunction()

{

int count = 5;

//Выводим пять раз значение count

while(count 0)

{

Console.WriteLine(count);

--count;

}

}

}

}

После завершения выполнения потоков, в консоле будет выведено 10 чисел.

Если же мы хотим, что бы каждый поток вел себя по-разному, то есть два решения. Первое - создание дополнительной функции для потока.

Например, вот так:

using System;

using System.Threading;

namespace SomeThreadsExample

{

class Program

{

static void Main(string[] args)

{

//Создаем первый поток

Thread thread1 = new Thread(ThreadFunction1);

thread1.Start();

//Создаем второй поток

Thread thread2 = new Thread(ThreadFunction2);

thread2.Start();

Console.Read();

}

static void ThreadFunction1()

{

//Просто выводим что-нибудь в консоль для наглядности

Console.WriteLine("Это первый поток!");

}

static void ThreadFunction2()

{

//Аналогично первому потоку

Console.WriteLine("Это второй поток!");

}

}

}

Второе решение более запутанное. Смысл в том, что бы используя один метод выполнять разные действия в разных потоках. Для этого, внутри метода потока нужно определить «кто я?». Как вариант, используем метод потока, который может принимать значения. В поток будем передавать булевый флаг. Если он равен истине - значит это первый поток, если ложь - значит второй. Метод потока сам будет определять «кто я?» и в зависимости от этого, выполнять разные действия.

Вот код:

using System;

using System.Threading;

namespace SomeThreadsExample

{

class Program

{

static void Main(string[] args)

{

Thread thread1 = new Thread(ThreadFunction);

thread1.Start(true);

Thread thread2 = new Thread(ThreadFunction);

thread2.Start(false);

Console.Read();

}

static void ThreadFunction(Object input)

{

//Преобразовываем входящий параметр в bool

bool flag = (bool)input;

//Если входящий флаг true - значит "я первый поток"

if(flag)

{

Console.WriteLine("Это первый поток!");

}

//Если входящий флаг false - значит "я второй поток"

else

{

Console.WriteLine("Это второй поток!");

}

}

}

}

Как работает данная программа? Каждый поток использует свой экземпляр метода. Если потоку выдали флаг true, значит он выполняет один код, если false - другой. Этот пример так же наглядно демонстрирует как работать с методами потоков, который принимают параметры.

Давайте разберем, как обмениваться данными между разными потоками. Перед тем, как я приведу пример с кодом, расскажу немного теории. Во всех многопоточных приложениях существует синхронизация данных. Когда два или больше потоков, пытаются взаимодействовать с какими либо данными одновременно - может возникнуть ошибка. Для этого в C# существует несколько способов синхронизации, но мы рассмотрим самый простой с помощью оператора lock.

Теперь давайте пример. Создадим отдельный класс, который будет создавать дополнительные потоки. Каждый поток будет работать с одной переменной (свойство класса). Для обеспечения сохранности данных мы будем использовать оператор lock. Его синтаксис очень простой:

lock(object1) { ... }

Как он работает? Когда один из потоков, доходит до оператора lock, он проверяет, не заблокирован ли object1. Если нет - выполняет указанные в скобках операторы. Если заблокирован - ждет, когда object1 будет разблокирован. Таким образом, оператор lock предотвращает одновременное обращение нескольких потоков к одним и тем же данным.

А вот и пример программы:

using System;

using System.Threading;

namespace ThreadingClass

{

class Program

{

static void Main(string[] args)

{

//Создаем объект нашего класса

Worker worker = new Worker();

//Запускаем создание потоков

worker.Run();

//Ждем ввода от пользователя, что бы не закрылась консоль

Console.Read();

}

}

//Класс, который создает потоки

class Worker

{

//Переменная для демонстрации работы оператора lock

private int value = 0;

//Переменная "локер", которая служит для блокировки value

private object valueLocker = new object();

//Метод запускающий потоки

public void Run()

{

for(int i = 0; i 5; ++i)

{

Thread thread = new Thread(ThreadFunction);

thread.Start();

}

}

private void ThreadFunction()

{

//Блокируем доступ к локеру

lock(valueLocker)

{

//Выводимзначение value

Console.WriteLine(value);

//Увеличиваем его на единицу

++value;

}

}

}

}

Я использовал отдельную переменную, для оператора lock, поскольку он не может заблокировать доступ к int переменной. А вообще, мне как то на хабре посоветовали всегда использовать «локеры» для блокировки других данных.

Теперь о программе. Каждый поток сначала выводит значение свойства value, а потом увеличивает его на единицу. Зачем же тут нужен оператор lock? Запустите программу. Она выведет по порядку «0 1 2 3 4?. Потоки по очереди выводят значение value, и все хорошо. Предположим, что оператора lock нету. Тогда может получиться следующее:

1. Первый поток выведет 0 на экран;

2. Второй поток выведет 0 на экран и увеличит значение на единицу;

3. Третий поток выведет 1на экран;

4. Первый поток увеличит значение на единицу;

5. Третий поток увеличит значение на единицу;

6. Второй поток выведет 3 на экран;

… и так далее. В результате мы получим, что-то вроде «0 0 1 3 4?. Это связано с тем, что процессор и планировщик задач не знают в каком порядке выполняться операции, поэтому они делают это оптимально с точки зрения выполнения программы, при этом логика нарушается.

Рекомендуем:

Новости
МТС запустила облачную платформу для выполнения рутинных процессов
CloudMTS запустил облачную платформу роботизации RPA CloudMTS для выполнения рутинных процессов на рабочем месте. Сервис создан на базе отечественного решения Primo RPA и, как говорится в известьи компании, позволит заказчикам в 10-20 раз прытче совершать повторяющиеся операции. Один робот способен выполнить работу 4-8 человек.
Дата публикации: 18.04.2024
Продажи умных колонок выросли на 33% в первом квартале 2024 года
Группа «М.Видео-Эльдорадо» продолжает отмечать уверенный рост категории умных колонок как на рынке России в целом, так и в собственных каналах продаж.В 2023 году рынок умных колонок с виртуальными помощниками в количественном выражении достиг рекордной отметки в 4,3 млн штук, в то время как в деньгах продажи этих устройств составили чуть более 34 млрд рублей, сообщили в пресс-службе «М.
Дата публикации: 18.04.2024
1234...
Статьи
МегаФон стал партнёром финансовой платформы Банки.ру
1 июня 2023 МегаФон и финансовая платформа Банки.ру (АО «Цифровые технологии») запускают партнёрство. Первый совместный проект позволит предоставить клиентам доступ к финансовым предложениям любого российского банка?участника платформы, независимо от наличия его отделения поблизости.
Автор: prteammf
Дата публикации: 30.07.2023
«МегаФон Облако» поможет учебным заведениям совершенствовать образовательный процесс
14 июня 2023 МегаФон предоставил виртуальную инфраструктуру Институту развития образования Свердловской области. Преподаватели, сотрудники и слушатели образовательного учреждения получили дополнительные возможности для развития дистанционных программ в безопасной облачной среде.
Автор: prteammf
Дата публикации: 30.07.2023
МегаФон разработает систему экомониторинга морской акватории Камчатского края
23 июня 2023 МегаФон стал партнёром Правительства Камчатского края в области обеспечения экологической безопасности морской среды. Оператор поможет внедрить технологии мониторинга для сохранения и восстановления морской экосистемы, а также предотвращения возможных природных и техногенных катастроф.
Автор: prteammf
Дата публикации: 30.07.2023
1234...
Вопросы
Отзывы
Информация
Разработка программ и автоматизация вашего бизнеса это основные направления нашей компании. Наше основное отличие это доступность и качество автоматизации.

Copyright © 2024
www.softbusiness.net
Контакты
Написать в отдел технической поддержки пользователей
По всем вопросам
обращаться
по телефону:
+7(918)3883-585