Асинхронная многопоточность

using System;
using log4net;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace dotNetGameServer.Models.Controllers
{
    public static class GameTimeController
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private static IServiceProvider serviceProvider;

        public static async Task InitialiseAsync(IServiceProvider serviceProviderGameMaster)
        {
            serviceProvider = serviceProviderGameMaster;

            await Task.Factory.StartNew(TickTimeElapsedAsync);
        }

        private static async Task TickTimeElapsedAsync()
        {
            while (true)
            {
                Log.Info("Tick...");
                await Task.Delay(1000);
            }
        }
    }
}

Я думал await Task.Factory.StartNew запускает новую задачу, а не поток. Как не как, в отладке мне выводит что строка Tick... отображается в разных потоках. То 4, потом в 8. Потом в 9 и т.д. Почему так происходит?


Ответы (1 шт):

Автор решения: VladD

Task.Factory.StartNew отправляет задачу TickTimeElapsedAsync выполняться на пул потоков (вы можете проверить это через Thread.CurrentThread.IsThreadPoolThread).

На пуле потоков await Task.Delay возвращает вас на случайный поток из пула, т. к. SynchronizationContext нулевой. Поэтому, да, поток каждый раз может быть новый.


Обратите внимание что Task.Factory.StartNew(TickTimeElapsedAsync) возвращает Task<Task>: таск, задачей которого является запустить таск TickTimeElapsedAsync. Поэтому ваш await Task.Factory.StartNew(TickTimeElapsedAsync); дожидается запуска таска, а не окончания. Если вы хотите таки дождаться окончания, используйте await await Task.Factory.StartNew(TickTimeElapsedAsync);:

  • Task.Factory.StartNew(TickTimeElapsedAsync) возвращает запускающий таск,
  • await Task.Factory.StartNew(TickTimeElapsedAsync) возвращает его результат, то есть запущенный таск, а
  • await await Task.Factory.StartNew(TickTimeElapsedAsync) дожидается запущенного таска.

Более «прямая» логика была бы, если воспользоваться функцией Task.Run: await Task.Run(TickTimeElapsedAsync) таки дожидается запущенного таска.

Дополнительное чтение по теме: Stephen Toub, Task.Run vs Task.Factory.StartNew.

→ Ссылка