C# 这两种方法中,哪一种是在WPF桌面应用程序中运行CPU密集型长时间运行任务的正确方法?

C# 这两种方法中,哪一种是在WPF桌面应用程序中运行CPU密集型长时间运行任务的正确方法?,c#,asynchronous,async-await,task,c#-9.0,C#,Asynchronous,Async Await,Task,C# 9.0,关于异步操作,我理解在实现中使用Task.Run是一种不好的做法,如果您要将库中的代码公开给其他地方使用,那么应该将这些决定留给库的调用者。这些信息可以在这里的“重复”问题中找到但这不是我要问的。我想澄清一下我们自己的代码,它在WPF桌面应用程序中,永远不会作为库公开给其他人使用 此外,我们的任务需要1-2分钟的CPU密集处理,如果这有什么区别的话 我的问题是,在我们的特殊情况下,使用后台线程使非异步函数异步是有意义的,还是我们只是使函数异步,然后用wait调用它 此外,长时间运行的函数本身不执

关于异步操作,我理解在实现中使用
Task.Run
是一种不好的做法,如果您要将库中的代码公开给其他地方使用,那么应该将这些决定留给库的调用者。这些信息可以在这里的“重复”问题中找到但这不是我要问的。我想澄清一下我们自己的代码,它在WPF桌面应用程序中,永远不会作为库公开给其他人使用

此外,我们的任务需要1-2分钟的CPU密集处理,如果这有什么区别的话

我的问题是,在我们的特殊情况下,使用后台线程使非异步函数异步是有意义的,还是我们只是使函数异步,然后用wait调用它

此外,长时间运行的函数本身不执行任何异步/等待操作。这纯粹是一个线性的CPU密集型任务,我不关心它在哪里运行,只关心它的返回值(和进度)被报告回主UI

这就是我要求澄清的问题

我在WPF桌面应用程序中直接使用带C#9的.NET 5.0(即,此代码不在库中,无法与其他人共享),如果有必要的话

这里有一些(为了清晰起见更新了!)示例代码说明了这一点

publicstaticvoidmain(){
//启动导入的本地函数
//注意:我读到我应该在这里使用'async void',而不是'async Task',因为
//这是一个顶级异步语句,我知道您需要它来进行适当的处理
//异常处理,对吗?
异步void startImport(){
Debug.WriteLine($“导入开始…IsBackground:”+
$“{Thread.CurrentThread.IsBackground}”);
变量进度=新进度(消息=>
Debug.WriteLine($“进度更新:{message}”);
//切换这些行上的注释和定义行
//CPUIntenseSequentialImport在有任务和无任务的情况下进行尝试。Run()
var result=wait Task.Run(()=>CPUIntenseSequentialImport(progress));
//var结果=等待CPUIntenseSequentialImport(进度);
Debug.WriteLine($“完成!结果为{result}-IsBackground:”+
$“{Thread.CurrentThread.IsBackground}”);
}
startImport();
Debug.WriteLine($“导入已启动…IsBackground:”+
$“{Thread.CurrentThread.IsBackground}”);
}
//您是否可以将其标记为async,然后对其使用wait,而不需要Task.Run(),
//或者因为它是一个长期运行的任务,您是否应该使用async标记它,并
//而是使用Task.Start()从池中获取自己的线程?
静态公共int CPUIntenseSequentialImport(IProgress进程){
//静态异步公共任务CPUIntenseSequentialImport(IProgress进程){
睡眠(1000);
进度报告($“导入的第一部分已完成-IsBackground:”+
$“{Thread.CurrentThread.IsBackground}”);
睡眠(1000);
进度报告($“导入的下一部分已完成-IsBackground:”+
$“{Thread.CurrentThread.IsBackground}”);
睡眠(1000);
进度报告($“导入完成的最后一部分-IsBackground:”+
$“{Thread.CurrentThread.IsBackground}”);
返回44;
}
当按原样运行时,您会得到这样的结果(注意更新不是从后台线程报告的)

导入开始…IsBackground:False
导入已开始…IsBackground:False
进度更新:完成导入的第一部分-IsBackground:False
进度更新:导入的下一部分完成-IsBackground:False
进度更新:完成导入的最后一部分-IsBackground:False
完成!结果是44-IsBackground:False
…但是,当您交换注释/未注释行时,您会得到这样的结果(请注意,更新是从后台线程报告的)

导入开始…IsBackground:False
导入已开始…IsBackground:False
进度更新:导入的第一部分完成-IsBackground:True
进度更新:导入的下一部分完成-IsBackground:True
进度更新:完成导入的最后一部分-IsBackground:True
完成!结果是44-IsBackground:False

这里实际上有很多东西需要解开,所以我会尽我所能回答

我理解使用任务是不好的做法。如果

事实上,这条规则有一个非常模糊的例外,但一般来说,
Task.Start
根本不应该使用。
Task.Run
是将工作推送到线程池线程的方法

我正在为WPF桌面应用程序中的我们自己的代码寻求澄清,它永远不会作为库公开给其他人使用

这是有价值的信息。归根结底,你自己的代码就是你自己的代码,即使你使用了
Task.Start
,这也取决于你。另一方面,好的实践变成了好的实践,因为人们发现它们使代码更易于维护。(至少,这是我坚持的理论——尽管有货物崇拜;)

具体地说,即使您没有在NuGet上分发库,您的代码也可以从将其视为库中获益在项目中,代码可能会从被视为在一个单独的层中受益。关注点分离和所有这些——长时间运行的处理代码不应该知道它是从UI线程调用的(并且,给定单元测试,它可能不知道)

使用后台线程使非异步函数异步是有意义的,还是我们只是使函数异步,然后用wait调用它?另外,长时间运行的函数本身不做任何异步/等待的事情

那么,我会说保持它同步。使它
异步的唯一原因是取消