C# 在后台线程上运行异步函数?

C# 在后台线程上运行异步函数?,c#,multithreading,asynchronous,async-await,task,C#,Multithreading,Asynchronous,Async Await,Task,我正在做一个包括服务器和客户端的项目。 客户端每秒向服务器发送一个UDP数据包,根据服务器的响应,它可能会打开与服务器的TCP连接(并从服务器接收文件),客户端程序有一个我不想阻止的GUI(在WPF MVVM中制作),在主窗口的构造函数上启动异步函数也是有问题的,因为构造函数不能是异步的。 所以我的问题是,我可以并且应该在后台线程上运行异步函数吗?如果异步在后台线程上,它是否会提高性能,我要说的是这些选项之间的区别: public MainWindow() { InitializeCom

我正在做一个包括服务器和客户端的项目。 客户端每秒向服务器发送一个UDP数据包,根据服务器的响应,它可能会打开与服务器的TCP连接(并从服务器接收文件),客户端程序有一个我不想阻止的GUI(在WPF MVVM中制作),在主窗口的构造函数上启动异步函数也是有问题的,因为构造函数不能是异步的。 所以我的问题是,我可以并且应该在后台线程上运行异步函数吗?如果异步在后台线程上,它是否会提高性能,我要说的是这些选项之间的区别:

public MainWindow()
{
    InitializeComponent();
    DirectoryViewModel DirectoryVM = new DirectoryViewModel();
    this.DataContext = DirectoryVM;
    DirectoryVM.StartListen(); //Unwanted as constructor won't finish
}

据我所见,我不应该像这样在后台线程上运行异步代码,但为什么呢?它不是比在后台线程上运行同步代码更快吗? 另外,我想这并没有什么不同,但在我的服务器上,我将创建一个持续运行的线程,侦听tcp连接并通过它发送文件,我是否应该使发送文件函数异步

谢谢

。。。在单独的线程上启动异步方法可以吗

我认为您从根本上误解了异步方法。调用异步方法不会创建新线程或将整个方法卸载到任何其他线程上。调用一个异步方法就像调用任何其他方法一样,但它在某一点上可能会将执行返回给调用方(以任务作为承诺),并在某一点上完成剩余的作业

虽然可以使用异步方法启动始终运行的周期侦听器,但这与它的目的背道而驰

当您调用一个异步方法时,您希望它能在合理的时间内运行到完成(这就是为什么您要等待它),但同时执行其他操作可能需要足够长的时间。在您的情况下,您应该显式地启动一个新的后台任务或线程,该线程为您执行定期检查。它可以是一个新线程,也可以是更好的新任务(例如task.Run)

据我所见,我不应该在后台线程上运行异步代码 像这样,但为什么

根据您的评论,
DirectoryVM.StartListen()
启动持续运行的侦听,而不必是异步的。除非它执行任何异步调用,否则它不是可以等待的

它不是比在后台线程上运行同步代码更快吗

异步不是关于速度,而是关于线程阻塞。不管线程是前台还是后台,使用异步方法进行I/O操作(如调用http端点或发送UDP数据包)总是有益的,如果线程在等待时可以做其他事情,或者阻塞该线程可能会导致其他问题

我想这也没什么不同,但在我的服务器上,我会创建一个 持续运行的线程,侦听tcp连接并将发送 文件,我应该使发送文件函数异步还是不异步

如果有益的话,你应该这样做。见上一部分

据我所见,我不应该像这样在后台线程上运行异步代码,但为什么呢

我不知道有任何建议不使用
Task.run
运行异步代码

应避免在ASP.NET上运行
Task.Run
(对于同步和异步代码),但这不适用于这里,因为您有一个GUI应用程序

它不是比在后台线程上运行同步代码更快吗

不,代码不会更快<代码>异步是关于释放线程,而不是运行得更快

我认为你所看到的担忧是忽视任务。使用
Task.Run
并忽略它返回的任务是有问题的,因为这是一种先发后忘的形式。因此,如果您的循环由于异常而失败,您的应用程序将永远不会知道。解决此问题的一种方法是
等待
异步void
方法中的任务。例如,当窗口的
已初始化时
事件处理程序:

async void Window_Initialized(object, EventArgs)
{
  await Task.Run(DirectoryVM.StartListen);
  // or, if asynchronous:
  await DirectoryVM.StartListenAsync();
}

你从哪里开始后台线程?我只能看到一个
任务。Run
。通常意味着一个单独的线程,很抱歉我没有理解。不执行任务。运行队列到线程池?第三个选项看起来更快,因为您没有等待它完成。当
MainWindow
退出时,
DirectoryVM.StartListenAsync
可能不完整,更糟糕的是,您无法知道它实际完成的时间。在构造函数中运行异步代码也是一个好主意。要获得好的答案,我们必须知道
StartListen[async]()
的功能。但看起来第一个片段就可以了。您希望这里有什么样的性能差异?我不确定我是否同意应该避免异步方法中的周期性操作。我看不出有什么问题。如果我调用一个异步方法,我不希望它有应用程序的生存期,比如说你有以下方法:
async Task DoForever(){while(true){wait SomeCall();wait Task.Delay(X);}}
现在,如果你知道这个方法的工作原理,你可以调用
DoForever()
但是调用
wait DoForever()
将阻止执行。我认为应该避免这种永远运行的异步方法。目前的
DoForever
方法(或者更好的
DoForeverAsync
)将返回一个火,并忘记不应该等待的任务。毫无疑问,等待这项任务会永远阻碍。想象有人在写
等待任务。延迟(-1)
,然后惊讶地发现下一行代码从未到达。这个人急需一些咖啡!一个可能更好的设计是接受
CancellationToken
参数,然后在应用程序的终结器上取消令牌并
等待任务,以保持整洁。谢谢你的回答。只是为了确定,这对我来说是有益的
public MainWindow()
{
    InitializeComponent();
    DirectoryViewModel DirectoryVM = new DirectoryViewModel();
    this.DataContext = DirectoryVM;
    Task.Run(async () => await DirectoryVM.StartListenAsync()); //Is it faster than the second option?
}
async void Window_Initialized(object, EventArgs)
{
  await Task.Run(DirectoryVM.StartListen);
  // or, if asynchronous:
  await DirectoryVM.StartListenAsync();
}