我对使用async修饰符进行异步编程并不熟悉。我试图弄清楚如何确保我的控制台应用程序的主要方法实际上是异步运行的。
class Program
{
static void Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = bs.GetList();
}
}
public class Bootstrapper {
public async Task<List<TvChannel>> GetList()
{
GetPrograms pro = new GetPrograms();
return await pro.DownloadTvChannels();
}
}
我知道这不是从“顶层”异步运行的。由于不可能在Main方法上指定异步修饰符,我如何在Main中异步运行代码?
下面的代码可以用来创建一个主异步。我已经调整它使用长时间运行的任务(了解更多信息在这里:https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-7.0)
它还实现了来自上述响应的取消令牌。
private static int Main(string[] args)
{
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = !cts.IsCancellationRequested;
cts.Cancel();
Console.WriteLine("CancellationRequested");
};
try
{
var task = new Task<int>(
() => MainAsync(args, cts.Token).GetAwaiter().GetResult(),
cts.Token,
TaskCreationOptions.LongRunning //https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-7.0
);
task.Start();
var exitCode = task.GetAwaiter().GetResult();
/*Or this.*/
//var exitCode = MainAsync(args, cts.Token).GetAwaiter().GetResult();
return exitCode;// MainAsync(args, cts.Token).GetAwaiter().GetResult();
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex);
return 1223; // Cancelled.
}
catch(Exception ex)
{
Console.WriteLine(ex);
return -1;
}
}
private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken)
{
await Something()
return;
}
在下面的例子中,我写了。你可以使用maxDegreeOfParallelism & numberOfIteration来理解/查看任务是如何处理的。学习TPL很好的起点!
private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken)
{
var infos = new ConcurrentBag<Info>();
var mySuperUselessService = new BigWorkload();
int numberOfSecond = 1;
int numberOfIteration = 25; //Experiment with this
int maxDegreeOfParallelism = 4; //Experiment with this
var simulateWorkTime = TimeSpan.FromSeconds(numberOfSecond);
var informations = Enumerable.Range(1, numberOfIteration)
.Select(x => new Info() { Index = x });
var count = informations.Count();
var chunkNeeded = Math.Round(count / Convert.ToDecimal(maxDegreeOfParallelism), MidpointRounding.ToPositiveInfinity);
var splashInfo = @$"
Press CTRL + C to cancel.
Processing {count} items, maxDegreeOfParallelism set to {maxDegreeOfParallelism}.
But it will be bound by the core on the machine {Environment.ProcessorCount}.
This operation should take ~{chunkNeeded * (numberOfSecond + 0.01m)}s
And will be starting test in 2s
";
Console.WriteLine(splashInfo);
await Task.Delay(TimeSpan.FromSeconds(2));
var parralelOptions = new ParallelOptions() { MaxDegreeOfParallelism = maxDegreeOfParallelism, CancellationToken = cancellationToken};
var stopwatch = new Stopwatch();
stopwatch.Start();
var forLoopTask = Parallel.ForEachAsync(informations, parralelOptions, async (info, token) =>
{
await mySuperUselessService.Simulate(simulateWorkTime, info);
Console.WriteLine(info);
infos.Add(info);
});
await forLoopTask;
stopwatch.Stop();
foreach (var grouped in infos.GroupBy(x => x.ManagedThreadId))
{
Console.WriteLine($"ThreadId: {grouped.Key}");
foreach (var item in grouped)
{
Console.WriteLine($"\t Index: {item.Index} {item.TaskCurrentId}");
}
}
Console.WriteLine($"NumberOfThread: {infos.GroupBy(x => x.ManagedThreadId).Count()}");
Console.WriteLine($"Elasped: {stopwatch.ElapsedMilliseconds / 1000d}s");
Console.WriteLine(splashInfo);
return 0;
}
我将添加一个所有其他答案都忽略了的重要功能:取消。
TPL的一大特点是支持取消功能,控制台应用有内置的取消功能(CTRL+C)。把它们结合在一起很简单。这是我如何构建所有异步控制台应用程序:
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
System.Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
cts.Cancel();
};
MainAsync(args, cts.Token).GetAwaiter.GetResult();
}
static async Task MainAsync(string[] args, CancellationToken token)
{
...
}
下面的代码可以用来创建一个主异步。我已经调整它使用长时间运行的任务(了解更多信息在这里:https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-7.0)
它还实现了来自上述响应的取消令牌。
private static int Main(string[] args)
{
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = !cts.IsCancellationRequested;
cts.Cancel();
Console.WriteLine("CancellationRequested");
};
try
{
var task = new Task<int>(
() => MainAsync(args, cts.Token).GetAwaiter().GetResult(),
cts.Token,
TaskCreationOptions.LongRunning //https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-7.0
);
task.Start();
var exitCode = task.GetAwaiter().GetResult();
/*Or this.*/
//var exitCode = MainAsync(args, cts.Token).GetAwaiter().GetResult();
return exitCode;// MainAsync(args, cts.Token).GetAwaiter().GetResult();
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex);
return 1223; // Cancelled.
}
catch(Exception ex)
{
Console.WriteLine(ex);
return -1;
}
}
private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken)
{
await Something()
return;
}
在下面的例子中,我写了。你可以使用maxDegreeOfParallelism & numberOfIteration来理解/查看任务是如何处理的。学习TPL很好的起点!
private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken)
{
var infos = new ConcurrentBag<Info>();
var mySuperUselessService = new BigWorkload();
int numberOfSecond = 1;
int numberOfIteration = 25; //Experiment with this
int maxDegreeOfParallelism = 4; //Experiment with this
var simulateWorkTime = TimeSpan.FromSeconds(numberOfSecond);
var informations = Enumerable.Range(1, numberOfIteration)
.Select(x => new Info() { Index = x });
var count = informations.Count();
var chunkNeeded = Math.Round(count / Convert.ToDecimal(maxDegreeOfParallelism), MidpointRounding.ToPositiveInfinity);
var splashInfo = @$"
Press CTRL + C to cancel.
Processing {count} items, maxDegreeOfParallelism set to {maxDegreeOfParallelism}.
But it will be bound by the core on the machine {Environment.ProcessorCount}.
This operation should take ~{chunkNeeded * (numberOfSecond + 0.01m)}s
And will be starting test in 2s
";
Console.WriteLine(splashInfo);
await Task.Delay(TimeSpan.FromSeconds(2));
var parralelOptions = new ParallelOptions() { MaxDegreeOfParallelism = maxDegreeOfParallelism, CancellationToken = cancellationToken};
var stopwatch = new Stopwatch();
stopwatch.Start();
var forLoopTask = Parallel.ForEachAsync(informations, parralelOptions, async (info, token) =>
{
await mySuperUselessService.Simulate(simulateWorkTime, info);
Console.WriteLine(info);
infos.Add(info);
});
await forLoopTask;
stopwatch.Stop();
foreach (var grouped in infos.GroupBy(x => x.ManagedThreadId))
{
Console.WriteLine($"ThreadId: {grouped.Key}");
foreach (var item in grouped)
{
Console.WriteLine($"\t Index: {item.Index} {item.TaskCurrentId}");
}
}
Console.WriteLine($"NumberOfThread: {infos.GroupBy(x => x.ManagedThreadId).Count()}");
Console.WriteLine($"Elasped: {stopwatch.ElapsedMilliseconds / 1000d}s");
Console.WriteLine(splashInfo);
return 0;
}