Я бы использовал System.Threading.Timer . Вы можете использовать метод Change, который использует TimeSpan, чтобы запускать его каждые 10 минут. Преимущество использования таймера состоит в том, что вы не создаете поток, который просто спит большую часть времени, что неэффективно. Таймер зарегистрирован в ОС, и в ожидании тайм-аута ресурсы не используются.
Имейте в виду, что он реентерабелен, поэтому, если ваш код занимает более 10 минут, он может снова запуститься в другом потоке.
Нет необходимости беспокоиться о потоках с этим решением, поскольку Timer возьмет поток ThreadPool и выполнит ваш обратный вызов для этого. И вернемся к моему комментарию о том, что ваш код занимает больше 10 минут, если ваш обратный вызов все еще выполняется через 10 минут, для запуска вашего таймера будет использоваться ДРУГОЙ поток пула потоков, поэтому в этом случае два потока будут одновременно выполнять ваш обратный вызов.
Использование Task не обязательно означает, что ваш код будет выполняться в другом потоке; это может, но это до планировщика, чтобы определить, стоит ли помещать в другой поток. Вы все еще можете использовать задачу в обратном вызове таймера; Возможно, вы захотите сделать это, если ваша Задача связана с вводом-выводом, потому что, когда вы, наконец, ожидаете Задачу, поток пула потоков будет освобожден, чтобы выполнять работу для других вещей, помещая запросы в пул. Когда ваша задача завершится, она получит другой (возможно, другой) поток для продолжения выполнения вашего кода обратного вызова таймера. Вы можете найти более подробную информацию о том, как Задачи работают, в других ответах.