компьютерное программирование Термин «поток» - это сокращение от потока выполнения, в котором процессор следует по указанному пути через ваш код. Концепция отслеживания более чем одного потока одновременно вводит тему многозадачности и многопоточности.
В приложении есть один или несколько процессов. Думайте о процессе как о программе, запущенной на вашем компьютере. Теперь у каждого процесса есть один или несколько потоков. Игровое приложение может иметь поток для загрузки ресурсов с диска, другой для искусственного интеллекта и другой для запуска игры в качестве сервера.
В .NET / Windows операционная система выделяет процессорное время для потока. Каждый поток отслеживает обработчики исключений и приоритет, с которым он работает, и у него есть место для сохранения контекста потока до его запуска. Контекст потока - это информация, которую поток должен возобновить.
Многозадачность с потоками
Потоки занимают немного памяти, а их создание занимает немного времени, поэтому обычно вы не хотите использовать много. Помните, они конкурируют за процессорное время. Если ваш компьютер имеет несколько процессоров, то Windows или .NET могут запускать каждый поток на другом процессоре, но если несколько потоков работают на одном и том же процессоре, тогда только один может быть активным одновременно, и переключение потоков занимает время.
Процессор запускает поток для нескольких миллионов инструкций, а затем переключается на другой поток. Все регистры ЦП, текущая точка выполнения программы и стек должны быть сохранены где-то для первого потока, а затем восстановлены из другого места для следующего потока.
Создание темы
В системе имен пространства. Резьбонарезной, вы найдете тип потока. Поток конструктора (ThreadStart) создает экземпляр потока. Однако в последнее время C # код, более вероятно передать лямбда-выражение, которое вызывает метод с любыми параметрами.
Если вы не уверены в лямбда-выражения, возможно, стоит проверить LINQ.
Вот пример потока, который создан и запущен:
используя Систему;
используя систему. Нарезание резьбы;
пространство имен ex1
{
Программа класса
{
публичная статическая пустота Write1 ()
{
Приставка. Написать ('1');
Нить. Сон (500);
}
static void Main (строка [] args)
{
var task = new Thread (Write1);
задача. Начало() ;
для (var i = 0; я <10; я ++)
{
Приставка. Написать ('0');
Приставка. Написать (задание. Является живым? «A»: «D»);
Нить. Сон (150);
}
Приставка. ReadKey ();
}
}
}
Все, что делает этот пример, пишет «1» в консоль. Основной поток записывает «0» в консоль 10 раз, после чего каждый раз следует «A» или «D» в зависимости от того, является ли другой поток все еще живым или мертвым.
Другой поток запускается только один раз и записывает «1». После полусекундной задержки в потоке Write1 () поток завершается и задача. IsAlive в основном цикле теперь возвращает "D."
Пул потоков и параллельная библиотека задач
Вместо того, чтобы создавать свой собственный поток, если вам не нужно это делать, используйте пул потоков. Начиная с .NET 4.0 у нас есть доступ к библиотеке параллельных задач (TPL). Как и в предыдущем примере, снова нам нужно немного LINQ, и да, это все лямбда-выражения.
Задачи использует Пул потоков за кулисами, но лучше использовать темы в зависимости от количества используемых.
Основным объектом в TPL является Задача. Это класс, который представляет асинхронную операцию. Самый распространенный способ начать работу с Задачей. Завод. StartNew как в:
Задача. Завод. StartNew (() => DoSomething ());
Где DoSomething () - это метод, который запускается. Можно создать задачу и не запускать ее сразу. В этом случае просто используйте Task следующим образом:
var t = new Task (() => Консоль. WriteLine ( "Hello"));
...
т. Начало();
Это не запускает поток, пока не будет вызван .Start (). В приведенном ниже примере пять задач.
используя Систему;
используя систему. Нарезание резьбы;
используя систему. Threading. Задания;
пространство имен ex1
{
Программа класса
{
общедоступная статическая пустота Write1 (int i)
{
Приставка. Написать (я);
Нить. Сон (50);
}
static void Main (строка [] args)
{
для (var i = 0; я <5; я ++)
{
значение var = i;
var runningTask = Задача. Завод. StartNew (() => Write1 (значение));
}
Приставка. ReadKey ();
}
}
}
Запустите его, и вы получите цифры от 0 до 4 в случайном порядке, например 03214. Это потому, что порядок выполнения задач определяется .NET.
Вы можете быть удивлены, зачем нужно значение var = i. Попробуйте удалить его и вызвать Write (i), и вы увидите нечто неожиданное, например 55555. Почему это? Это потому, что задача показывает значение i во время выполнения задачи, а не во время ее создания. Создавая новый переменная каждый раз в цикле каждое из пяти значений правильно сохраняется и выбирается.