Одним из важнейших классов, находящихся в пространстве имени System.Threading, является класс Timer . Данный класс позволяет запускать определенные действия по истечению некоторого периода времени.
Например, нам надо запускать какой-нибудь метод через каждые 2000 миллисекунд, то есть раз в две секунды:
class Program < static void Main(string[] args) < int num = 0; // устанавливаем метод обратного вызова TimerCallback tm = new TimerCallback(Count); // создаем таймер Timer timer = new Timer(tm, num, 0, 2000); Console.ReadLine(); >public static void Count(object obj) < int x = (int)obj; for (int i = 1; i < 9; i++, x++) < Console.WriteLine($»»); > > >
Первым делом создается объект делегата TimerCallback, который в качестве параметра принимает метод. Причем данный метод должен в качестве параметра принимать объект типа object .
И затем создается таймер. Данная перегрузка конструктора таймера принимает четыре параметра:
- объект делегата TimerCallback
- объект, передаваемый в качестве параметра в метод Count
- количество миллисекунд, через которое таймер будет запускаться. В данном случае таймер будет запускать немедленно после создания, так как в качестве значения используется 0
- интервал между вызовами метода Count
- Глава 1. Введение в C#
- Язык C# и платформа .NET
- Первая программа на C# с .NET CLI
- Начало работы с Visual Studio. Первая программа
- Первая программа на MacOS
- Первая программа на Linux
- Первое приложение в WSL
- Структура программы
- Переменные и константы
- Литералы
- Типы данных
- Консольный ввод-вывод
- Арифметические операции
- Поразрядные операции
- Операции присваивания
- Преобразования базовых типов данных
- Условные выражения
- Конструкция if..else и тернарная операция
- Циклы
- Массивы
- Задачи с массивами
- Методы
- Параметры методов
- Возвращение значения и оператор return
- Передача параметров по ссылке и значению. Выходные параметры
- Массив параметров и ключевое слово params
- Рекурсивные функции
- Локальные функции
- Конструкция switch
- Перечисления enum
- Классы и объекты
- Конструкторы, инициализаторы и деконструкторы
- Класс Program и метод Main. Программы верхнего уровня
- Структуры
- Типы значений и ссылочные типы
- Область видимости (контекст) переменных
- Пространства имен
- Глобальные пространства имен
- Подключение пространств имен по умолчанию
- Создание библиотеки классов в Visual Studio
- Создание библиотеки классов с помощью .NET CLI
- Модификаторы доступа
- Свойства
- Перегрузка методов
- Статические члены и модификатор static
- Установка пакетов Nuget
- Константы, поля и структуры для чтения
- Null и ссылочные типы
- Null и значимые типы
- Проверка на null, операторы ?. и ??
- Псевдонимы типов и статический импорт
- Наследование
- Преобразование типов
- Виртуальные методы и свойства
- Скрытие методов и свойств
- Различие переопределения и скрытия методов
- Абстрактные классы
- Класс System.Object и его методы
- Обобщенные типы
- Ограничения обобщений
- Наследование обобщенных типов
- Конструкция try..catch..finally
- Блок catch и фильтры исключений
- Типы исключений. Класс Exception
- Генерация исключения и оператор throw
- Создание классов исключений
- Поиск блока catch при обработке исключений
- Делегаты
- Применение делегатов
- Анонимные методы
- Лямбды
- События
- Ковариантность и контравариантность делегатов
- Делегаты Action, Predicate и Func
- Замыкания
- Определение интерфейсов
- Применение интерфейсов
- Явная реализация интерфейсов
- Реализация интерфейсов в базовых и производных классах
- Наследование интерфейсов
- Интерфейсы в обобщениях
- Копирование объектов. Интерфейс ICloneable
- Сортировка объектов. Интерфейс IComparable
- Ковариантность и контравариантность обобщенных интерфейсов
- Определение операторов
- Перегрузка операций преобразования типов
- Индексаторы
- Переменные-ссылки и возвращение ссылки
- Методы расширения
- Частичные классы и методы
- Анонимные типы
- Кортежи
- Records
- Паттерн типов
- Паттерн свойств
- Паттерны кортежей
- Позиционный паттерн
- Реляционный и логический паттерны
- Паттерны списков
- Список List
- Двухсвязный список LinkedList
- Очередь Queue
- Стек Stack
- Словарь Dictionary
- Класс ObservableCollection
- Интерфейсы IEnumerable и IEnumerator
- Итераторы и оператор yield
- Строки и класс System.String
- Операции со строками
- Форматирование и интерполяция строк
- Класс StringBuilder
- Регулярные выражения
- Структура DateTime
- Форматирование дат и времени
- DateOnly и TimeOnly
- Отложенная инициализация и тип Lazy
- Математические вычисления и класс Math
- Преобразование типов и класс Convert
- Класс Array и массивы
- Span
- Индексы и диапазоны
- Введение в многопоточность. Класс Thread
- Создание потоков. Делегат ThreadStart
- Потоки с параметрами и ParameterizedThreadStart
- Синхронизация потоков
- Мониторы
- Класс AutoResetEvent
- Мьютексы
- Семафоры
- Задачи и класс Task
- Работа с классом Task
- Задачи продолжения
- Класс Parallel
- Отмена задач и параллельных операций. CancellationToken
- Асинхронные методы, async и await
- Возвращение результата из асинхронного метода
- Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny
- Обработка ошибок в асинхронных методах
- Асинхронные стримы
- Основы LINQ
- Проекция данных
- Фильтрация коллекции
- Сортировка
- Объединение, пересечение и разность коллекций
- Агрегатные операции
- Получение части коллекции
- Группировка
- Соединение коллекций
- Проверка наличия и получение элементов
- Отложенное и немедленное выполнение LINQ
- Делегаты в запросах LINQ
- Введение в Parallel LINQ. Метод AsParallel
- Метод AsOrdered
- Обработка ошибок и отмена параллельных операции
- Введение в рефлексию. Класс System.Type
- Применение рефлексии и исследование типов
- Исследование методов и конструкторов с помощью рефлексии
- Исследование полей и свойств с помощью рефлексии
- Динамическая загрузка сборок и позднее связывание
- Атрибуты в .NET
- DLR в C#. Ключевое слово dynamic
- DynamicObject и ExpandoObject
- Использование IronPython в .NET
- Сборщик мусора в C#
- Финализируемые объекты. Метод Dispose
- Конструкция using
- Указатели
- Указатели на структуры, члены классов и массивы
- Работа с дисками
- Работа с каталогами
- Работа с файлами. Классы File и FileInfo
- FileStream. Чтение и запись файла
- Чтение и запись текстовых файлов. StreamReader и StreamWriter
- Бинарные файлы. BinaryWriter и BinaryReader
- Архивация и сжатие файлов
- Сериализация в JSON. JsonSerializer
- XML-Документы
- Работа с XML с помощью System.Xml
- Изменение XML-документа
- XPath
- Linq to Xml. Создание Xml-документа
- Выборка элементов в LINQ to XML
- Изменение документа в LINQ to XML
- Сериализация в XML. XmlSerializer
- Процессы
- Домены приложений
- AssemblyLoadContext и динамическая загрузка и выгрузка сборок
- Нововведения в C# 11
- Нововведения в C# 12
Источник: metanit.com
Как написать программу таймер в блокноте
ПИШЕМ СВОЙ ЛЕГКИЙ ТАЙМЕР НА PYTHON ЗА 5 МИНУТ
Реализация таймера на C++11
Понадобилось мне реализовать на современном С++ таймер, да не простой… В статье разобраны несколько вариантов таймера (от простого к интересному).
Простейший таймер на C++
Итак, самый простой таймер должен подождать заданное время, а потом вызвать нужную нам функцию. При этом возможны два варианта: приостановиться может основной поток или задержка и вызов функции могут быть вынесены в отдельный поток. Реализация:
// timer.hpp: #ifndef TIMERHPP #define TIMERHPP #include #include class Timer < public: Timer(); void add(std::chrono::milliseconds delay, std::functioncallback, bool asynchronous = true); >; #endif
В этом простейшем случае класс конечно не особо нужен — я тут его оставил для «совместимости» со следующими таймерами из статьи. Функция add принимает задержку, функцию, которую надо вызвать и флаг, задающий асинхронность.
// timer.cpp #include «timer.hpp» #include Timer::Timer() < >void Timer::add(std::chrono::milliseconds delay, std::function callback, bool asynchronous) < if (asynchronous) < std::thread([=]() < std::this_thread::sleep_for(std::chrono::milliseconds(delay)); callback(); >).detach(); > else < std::this_thread::sleep_for(std::chrono::milliseconds(delay)); callback(); >>
Если требуется асинхронное выполнение — создается новый объект потока и для него вызывается метод detach() , за счет этого поток работает независимо от основного потока. Функция std::this_thread::sleep_for останавливает поток на заданное время (в нашем случае задается в миллисекундах), после задержки вызывается наша callback-функция.
// main.cpp #include «timer.hpp» #include void foo() < std::cout void bar() < std::cout int main() < Timer timer; timer.add(std::chrono::milliseconds(1000), bar, true); timer.add(std::chrono::milliseconds(500), foo); timer.add(std::chrono::milliseconds(2000), []<>, false); >
В функции main в таймер добавляются три функции. Первой добавляется bar , однако у этой функции задана большая задержка, чем у foo — поэтому выполнится она позже. Третья функция добавляется асинхронной чтобы затормозить основной поток до тех пор, пока не завершат выполнение два других потока. Результат работы программы:
Обратите внимание, что функция add таймера принимает на вход функцию типа void(void) . Я не стал заморачиваться с шаблонами, т.к. если вам понадобится добавить функцию другого типа или даже вызов функции класса — вы всегда можете поместить туда лямбда-функцию, выполняющую нужный вызов.
Более сложный таймер
У меня возникла специфическая задача — таймер в один момент времени должен выполнять только одну функцию — предыдущая функция должна быть остановлена (не должна она быть вызвана когда завершится sleep_for ). Казалось бы — задача не сильно отличается от предыдущей, однако, std::thread не позволяет остановить поток, тем более, отсоединенный (после вызова detach ). Конечно, после выполнения sleep_for мы можем проверить не были ли добавлены другие функции, но это не эффективно. Мой код должен был быть размещен на сервере и если на него придет, скажем, 1000 запросов — то при таком подходе будет создана 1000 потоков, что уже может положить систему. Лучший вариант, что я смог придумать — «тормозить» единственный поток на небольшое время, а при добавлении функции лишь изменять callback и параметры времени. Задача очень специфическая, но имеет интересную реализацию (можно чему-нибудь научиться):
// timer.hpp #ifndef TIMERHPP #define TIMERHPP #include #include #include class Timer < public: Timer(); void add(std::chrono::milliseconds delay, std::functioncallback); private: bool m_is_running; std::mutex m_changing_mutex; std::chrono::milliseconds m_delay; std::chrono::high_resolution_clock::time_point m_start; std::function m_callback; >; #endif
- m_is_running хранит состояние таймера — true если в таймер добавлена функция, которую надо выполнить (она еще не была выполнена);
- m_delay хранит задержку выполнения текущей функции;
- m_start хранит время, когда в таймер была добавлена последняя функция;
- m_callback — последняя добавленная в таймер функция;
- все описанные выше параметры являются общими для главного потока и потока, замеряющего время — обращения к ним должна быть защищены мьютексом m_changing_mutex .
// timer.cpp #include «timer.hpp» #include Timer::Timer() : m_is_running(false) < std::thread([=]() < while (true) < std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guardlock(m_changing_mutex); if (m_is_running std::chrono::high_resolution_clock::now() — m_start > m_delay) < m_is_running = false; m_callback(); >> >).detach(); > void Timer::add(std::chrono::milliseconds delay, std::function callback) < std::lock_guardlock(m_changing_mutex); m_callback = callback; m_is_running = true; m_start = std::chrono::high_resolution_clock::now(); m_delay = delay; >
Конструктор создает поток, выполняющий замер времени и отсоединяет его от основного потока выполнения. Вспомогательный поток содержит вечный цикл, в котором выполняется остановка на 100 миллисекунд, захват ресурса (блокирует объект мьютекса), проверка условия необходимости выполнения (таймер запущен и прошло нужное количество времени), запуск функции на выполнение. Возможно, в ваших задачах будет иметь смысл выполнять функцию m_callback() в отдельном потоке.
Функция добавления функции в таймер захватывает ресурс (работает с тем же муьютексом) и затем изменяет все параметры.
В этом коде я предлагаю обратить внимание на работу с мьютексом (для тех, кто раньше на работал с std::lock_guard ) — это выглядит так здорово потому, что реализована идиома RAII (этот же принцип заложен в умные указатели типа unique_ptr). Так, функция add блокирует мьютекс создавая объект std::lock_guard , при этом объект создается на стеке, т.е. при выходе из функции (в том числе при возникновении исключения) ресурс освобождается. Аналогичным образом ресурс захватывается начиная с создания объекта std::lock_guard до конца итерации во вспомогательном потоке, замеряющем время.
Про замер времени можно прочитать в статье «Замерить время работы функции на С++«.
Источник: pro-prof.com
Руководство. Добавление таймера в приложение WinForms для математического теста
Область применения:Visual Studio
Visual Studio для Mac
Visual Studio Code
В этой серии из четырех учебников вы создадите математический тест. Тест содержит четыре случайных арифметических примера, которые игрок должен решить в течение определенного времени.
В тесте используется элемент управления Timer. Код, лежащий в основе этого элемента управления, отслеживает прошедшее время и проверяет ответы игрока.
В этом третьем учебнике вы научитесь следующему:
- Добавление элемента управления Timer.
- Добавление обработчика событий для таймера.
- Написание кода для проверки ответов игрока, вывода сообщений и указания правильных ответов.
Предварительные требования
Этот учебник построен на предыдущих, начиная с учебника Создание приложения WinForms с математическим тестом. Если вы не изучили эти учебники, начните с них.
Добавление таймера с обратным отсчетом
Для наблюдения за временем в ходе теста используется компонент «Таймер». Кроме того, требуется переменная для хранения оставшегося времени.
-
Добавьте целочисленную переменную с именем timeLeft так же, как вы объявляли переменные в предыдущих учебниках. Разместите объявление timeLeft сразу после других объявлений. Код должен выглядеть так, как показано ниже.
public partial class Form1 : Form < // Create a Random object called randomizer // to generate random numbers. Random randomizer = new Random(); // These integer variables store the numbers // for the addition problem. int addend1; int addend2; // These integer variables store the numbers // for the subtraction problem. int minuend; int subtrahend; // These integer variables store the numbers // for the multiplication problem. int multiplicand; int multiplier; // These integer variables store the numbers // for the division problem. int dividend; int divisor; // This integer variable keeps track of the // remaining time. int timeLeft;
Public Class Form1 ‘ Create a Random object called randomizer ‘ to generate random numbers. Private randomizer As New Random ‘ These integer variables store the numbers ‘ for the addition problem. Private addend1 As Integer Private addend2 As Integer ‘ These integer variables store the numbers ‘ for the subtraction problem. Private minuend As Integer Private subtrahend As Integer ‘ These integer variables store the numbers ‘ for the multiplication problem. Private multiplicand As Integer Private multiplier As Integer ‘ These integer variables store the numbers ‘ for the division problem.
Private dividend As Integer Private divisor As Integer ‘ This integer variable keeps track of the ‘ remaining time. Private timeLeft As Integer
Используйте элемент управления языка программирования в правом верхнем углу этой страницы, чтобы просмотреть фрагмент кода на C# или Visual Basic.
- В конструкторе Windows Forms переместите элемент управления Timer из категории Компоненты на панели элементов в форму. Элемент управления появляется в серой области в нижней части окна конструктора.
- Щелкните в форме только что добавленный значок timer1 и установите его свойство Interval равным 1000. Поскольку этот интервал измеряется в миллисекундах, при значении 1000 таймер создает событие Tick каждую секунду.
Проверка ответов
Поскольку таймер создает событие Tick каждую секунду, имеет смысл проверять истекшее время в обработчике событий Tick. Также целесообразно проверять ответы в этом обработчике событий. Если время истекло или ответы указаны правильно, тест должен завершиться.
Перед написанием этого обработчика событий добавьте метод CheckTheAnswer() , чтобы определить, верны ли ответы на арифметические примеры. Этот метод должен располагаться в строке с другими методами, например StartTheQuiz() . Код должен выглядеть так, как показано ниже.
/// /// Check the answers to see if the user got everything right. /// /// True if the answer’s correct, false otherwise. private bool CheckTheAnswer() < if ((addend1 + addend2 == sum.Value) (minuend — subtrahend == difference.Value) (multiplicand * multiplier == product.Value) (dividend / divisor == quotient.Value)) return true; else return false; >
»’ »’ Check the answers to see if the user got everything right. »’ »’ True if the answer’s correct, false otherwise. »’ Public Function CheckTheAnswer() As Boolean If addend1 + addend2 = sum.Value AndAlso minuend — subtrahend = difference.Value AndAlso multiplicand * multiplier = product.Value AndAlso dividend / divisor = quotient.Value Then Return True Else Return False End If End Function
Этот метод определяет ответы на арифметические примеры и сравнивает результаты со значениями в элементах управления NumericUpDown. В этом коде:
- Версия на Visual Basic использует ключевое слово Function вместо обычного ключевого слова Sub , потому что этот метод возвращает значение.
- Так как простого способа ввести знак умножения (×) и знак деления (÷) с клавиатуры нет, в языках C# и Visual Basic используется звездочка (*) для умножения и косая черта (/) для деления.
- В C# — это оператор logical and . Эквивалентный оператор в языке Visual Basic — AndAlso . Оператор logical and используется для проверки того, имеет ли значение true более одного условия. В этом случае, если все значения верны, метод возвращает значение true . В противном случае метод возвращает значение false .
- Инструкция if использует свойство Value элемента управления NumericUpDown для доступа к текущему значению элемента управления. В следующем разделе вы используете то же свойство для вывода правильного ответа в каждом элементе управления.
Добавление обработчика событий в таймер
Теперь, когда у вас есть способ проверить ответы, можно написать код для обработчика событий Tick. Этот код выполняется каждую секунду после того, как таймер создаст событие Tick. Этот обработчик событий проверяет ответы игрока, вызывая метод CheckTheAnswer() . Он также проверяет, сколько времени теста уже истекло.
-
Двойным щелчком выберите в форме элемент управления Timer либо выделите его и нажмите клавишу ВВОД. Эти действия добавляют обработчик событий Tick к таймеру. Откроется редактор кода, в котором отобразится метод обработчика Tick.
Добавьте в новый метод обработчика событий следующие операторы.
private void timer1_Tick(object sender, EventArgs e) < if (CheckTheAnswer()) < // If CheckTheAnswer() returns true, then the user // got the answer right. Stop the timer // and show a MessageBox. timer1.Stop(); MessageBox.Show(«You got all the answers right!», «Congratulations!»); startButton.Enabled = true; >else if (timeLeft > 0) < // If CheckTheAnswer() returns false, keep counting // down.
Decrease the time left by one second and // display the new time left by updating the // Time Left label. timeLeft = timeLeft — 1; timeLabel.Text = timeLeft + » seconds»; >else < // If the user ran out of time, stop the timer, show // a MessageBox, and fill in the answers. timer1.Stop(); timeLabel.Text = «Time’s up!»; MessageBox.Show(«You didn’t finish in time.», «Sorry!»); sum.Value = addend1 + addend2; difference.Value = minuend — subtrahend; product.Value = multiplicand * multiplier; quotient.Value = dividend / divisor; startButton.Enabled = true; >>
Private Sub Timer1_Tick() Handles Timer1.Tick If CheckTheAnswer() Then ‘ If CheckTheAnswer() returns true, then the user ‘ got the answer right.
Stop the timer ‘ and show a MessageBox. Timer1.Stop() MessageBox.Show(«You got all of the answers right!», «Congratulations!») startButton.Enabled = True ElseIf timeLeft > 0 Then ‘ If CheckTheAnswer() returns false, keep counting ‘ down. Decrease the time left by one second and ‘ display the new time left by updating the ‘ Time Left label. timeLeft -= 1 timeLabel.Text = timeLeft seconds» Else ‘ If the user ran out of time, stop the timer, show ‘ a MessageBox, and fill in the answers. Timer1.Stop() timeLabel.Text = «Time’s up!» MessageBox.Show(«You didn’t finish in time.», «Sorry!») sum.Value = addend1 + addend2 difference.Value = minuend — subtrahend product.Value = multiplicand * multiplier quotient.Value = dividend / divisor startButton.Enabled = True End If End Sub
Этот метод выполняется каждую секунду теста. Код сначала проверяет значение, которое возвращает CheckTheAnswer() .
- Если все ответы верны, это значение равно true , и тест завершается:
- Таймер останавливается.
- Появится поздравительное сообщение.
- Свойству Enabled элемента управления startButton устанавливается значение true , чтобы игрок мог заново запустить тест.
- Если эта переменная больше 0, таймер вычитает 1 из timeLeft. Затем он обновляет свойство Text элемента управления timeLabel, чтобы показать игроку, сколько осталось секунд.
- Если времени не остается, таймер останавливается и изменяет текст timeLabel на time’s up! В окне сообщения будет объявлено, что тест закончен, и появятся ответы. Кнопка старта снова станет доступной.
Запуск таймера
Чтобы запустить таймер при запуске теста, добавьте в конец метода StartTheQuiz() три строки, как показано в следующем примере.
/// /// Start the quiz by filling in all of the problem /// values and starting the timer. /// public void StartTheQuiz() < // Fill in the addition problem. // Generate two random numbers to add. // Store the values in the variables ‘addend1’ and ‘addend2’. addend1 = randomizer.Next(51); addend2 = randomizer.Next(51); // Convert the two randomly generated numbers // into strings so that they can be displayed // in the label controls. plusLeftLabel.Text = addend1.ToString(); plusRightLabel.Text = addend2.ToString(); // ‘sum’ is the name of the NumericUpDown control. // This step makes sure its value is zero before // adding any values to it. sum.Value = 0; // Fill in the subtraction problem. minuend = randomizer.Next(1, 101); subtrahend = randomizer.Next(1, minuend); minusLeftLabel.Text = minuend.ToString(); minusRightLabel.Text = subtrahend.ToString(); difference.Value = 0; // Fill in the multiplication problem. multiplicand = randomizer.Next(2, 11); multiplier = randomizer.Next(2, 11); timesLeftLabel.Text = multiplicand.ToString(); timesRightLabel.Text = multiplier.ToString(); product.Value = 0; // Fill in the division problem. divisor = randomizer.Next(2, 11); int temporaryQuotient = randomizer.Next(2, 11); dividend = divisor * temporaryQuotient; dividedLeftLabel.Text = dividend.ToString(); dividedRightLabel.Text = divisor.ToString(); quotient.Value = 0; // Start the timer. timeLeft = 30; timeLabel.Text = «30 seconds»; timer1.Start(); >
»’ »’ Start the quiz by filling in all of the problem »’ values and starting the timer. »’ »’ Public Sub StartTheQuiz() ‘ Fill in the addition problem. ‘ Generate two random numbers to add. ‘ Store the values in the variables ‘addend1’ and ‘addend2’. addend1 = randomizer.Next(51) addend2 = randomizer.Next(51) ‘ Convert the two randomly generated numbers ‘ into strings so that they can be displayed ‘ in the label controls. plusLeftLabel.Text = addend1.ToString() plusRightLabel.Text = addend2.ToString() ‘ ‘sum’ is the name of the NumericUpDown control. ‘ This step makes sure its value is zero before ‘ adding any values to it. sum.Value = 0 ‘ Fill in the subtraction problem. minuend = randomizer.Next(1, 101) subtrahend = randomizer.Next(1, minuend) minusLeftLabel.Text = minuend.ToString() minusRightLabel.Text = subtrahend.ToString() difference.Value = 0 ‘ Fill in the multiplication problem. multiplicand = randomizer.Next(2, 11) multiplier = randomizer.Next(2, 11) timesLeftLabel.Text = multiplicand.ToString() timesRightLabel.Text = multiplier.ToString() product.Value = 0 ‘ Fill in the division problem. divisor = randomizer.Next(2, 11) Dim temporaryQuotient As Integer = randomizer.Next(2, 11) dividend = divisor * temporaryQuotient dividedLeftLabel.Text = dividend.ToString() dividedRightLabel.Text = divisor.ToString() quotient.Value = 0 ‘ Start the timer. timeLeft = 30 timeLabel.Text = «30 seconds» Timer1.Start() End Sub
Теперь при запуске теста переменная timeLeft устанавливается в значение 30, а свойство Text элемента управления timeLabel — 30 секунд. После этого метод Start() элемента управления Timer начинает обратный отсчет.
Запустите приложение.
- Сохраните и выполните программу.
- Нажмите Начать тест. Таймер начинает обратный отсчет. Когда время истечет, тест закончится и появятся ответы.
- Запустите еще один тест и предоставьте правильные ответы на арифметические примеры. При правильном ответе в течение отведенного времени откроется окно сообщения, кнопка запуска станет доступной, а таймер остановится.
Дальнейшие действия
Перейдите к следующему учебнику, чтобы узнать, как настроить математический тест.
Источник: learn.microsoft.com