C# 等待自定义函数

C# 等待自定义函数,c#,.net,multithreading,asynchronous,C#,.net,Multithreading,Asynchronous,我试图了解C#中的新异步功能,到目前为止,我注意到的最奇怪的事情是,异步功能的每个示例都有一个函数等待在框架中定义的另一个异步函数,但它们都没有自定义代码 例如,我想从文本文件中的每一行创建一个对象,但要异步,以便UI线程不会冻结: async Task Read() { string[] subjectStrings = File.ReadAllLines(filePath); for (int i = 0; i < subjectStrings.Length; i++)

我试图了解C#中的新异步功能,到目前为止,我注意到的最奇怪的事情是,异步功能的每个示例都有一个函数等待在框架中定义的另一个异步函数,但它们都没有自定义代码

例如,我想从文本文件中的每一行创建一个对象,但要异步,以便UI线程不会冻结:

async Task Read()
{
    string[] subjectStrings = File.ReadAllLines(filePath);
    for (int i = 0; i < subjectStrings.Length; i++)
    {
        Task<Subject> function = new Task<Subject>(code => new Subject((string)code), subjectStrings[i]);
        try
        {
            Subject subject = await function;
            subjects.Add(subject);
        }
        catch (Exception ex)
        {
            debugWriter.Write("Error in subject " + subjectStrings[i]);
            continue;
        }
    }
}
异步任务读取()
{
string[]subjectStrings=File.ReadAllLines(filePath);
for(int i=0;i新主题((字符串)代码),主题字符串[i]);
尝试
{
主题=等待功能;
主题。添加(主题);
}
捕获(例外情况除外)
{
debugWriter.Write(“主题中的错误”+主题字符串[i]);
继续;
}
}
}
如您所见,我定义了一个任务,该任务基于文本文件中的一行创建一个新的
主题
对象,然后等待该任务。如果我这样做,调试器将到达
await
行,然后停止。据我所知,不再运行代码

如果我使用的是旧的异步特性,我只需要使用Task.ContinueWith()并添加一个回调lambda,将主题添加到列表中,然后就可以开始了

因此,我的问题是:

  • 为什么这个代码不起作用?如何创建一个本身不使用任何异步方法的自定义异步方法
  • 您应该如何使用异步方法?除非在异步函数中,否则不能使用
    await
    ,并且不应该在没有await的情况下调用异步方法,那么如何首先从同步方法调用该方法呢
  • 你没有开始这项任务,所以它永远不会完成

    使用
    Task。运行
    而不是
    newtask
    ,它将为您创建并启动任务


    请注意,您仍在同步读取文件,这并不理想。。。如果你的
    主题
    构造函数真的需要那么长时间才能完成,我会质疑它是否应该是一个构造函数。

    你把等待和工作混淆了。等待使用异步
    /
    等待
    ,工作不使用。这仍然意味着您可以等待CPU限制的任务,但必须手动运行,例如:

    var result = await Task.Run(YourLongOperation);
    
    帮助我从直觉上理解这一点的一个区别是,等待是合作的——我自愿放弃我的CPU时间份额,因为我实际上并不需要它另一方面,工作必须并行运行

    在正常情况下,仅使用固有的异步
    async
    /
    await
    s,不必超过您开始使用的单个线程。将CPU绑定的操作与I/O绑定的操作结合起来通常是个坏主意(CPU绑定的操作将阻塞,除非显式并行运行任务)

    为什么这个代码不起作用?如何创建一个本身不使用任何异步方法的自定义异步方法

    使用
    wait Task.Run
    wait Task.Factory.StartNew
    创建并运行任务。调用
    newtask
    将创建一个尚未启动的任务。在大多数情况下,这是不必要的,但您可以对以这种方式创建的任务调用
    Start

    您应该如何使用异步方法?除非在异步函数中,否则不能使用await,并且不应该在没有await的情况下调用异步方法,那么如何首先从同步方法调用该方法呢

    适当的“根”异步调用取决于应用程序的类型:

    • 在控制台应用程序中:
      等待
      返回的
      任务

    • 在GUI应用程序中:使用
      异步void
      事件处理程序

    • 在ASP.NET MVC中:控制器可以返回
      任务

    如何创建一个本身不使用任何异步方法的自定义异步方法


    你没有。如果方法没有异步工作要做,那么它应该是同步的;它不应该是异步的

    public async Task<IEnumerable<Subject>> ReadAsync(string filePath)
    {
        // call with 'this' instead of 'File'
        string[] subjectStrings = await this.ReadAllLinesAsync(filePath);
    
        return Parse(subjectStrings);
    }
    
    在核心,所有的
    async
    方法归结为两种方法之一。它们要么通过类似于
    Task.Run
    (不建议用于库代码)的方式将工作排队到线程池,要么通过
    TaskCompletionSource
    或快捷方式(如
    Task.Factory.fromsync
    )执行真正的异步工作

    public async Task<IEnumerable<Subject>> ReadAsync(string filePath)
    {
        // call with 'this' instead of 'File'
        string[] subjectStrings = await this.ReadAllLinesAsync(filePath);
    
        return Parse(subjectStrings);
    }
    
    您应该如何使用异步方法?除非在异步函数中,否则不能使用await,并且不应该在没有await的情况下调用异步方法,那么如何首先从同步方法调用该方法呢

    你没有。理想情况下,您应该始终保持
    async
    。控制台应用程序是此规则的例外;它们必须有一个同步
    Main
    。但是,对于WinForms、WPF、Silverlight、Windows应用商店、ASP.NET MVC、WebAPI、SignalR、iOS、Android和Windows Phone应用程序以及单元测试,您应该始终使用异步


    您可以通过
    wait
    和组合器(如
    Task.WhenAll
    Task.whenay
    )使用
    async
    方法。这是使用
    async
    方法最常见的方法,但不是唯一的方法;e、 例如,您可以调用
    async
    方法,并将其作为
    IObservable

    使用,我不会用另一个技术解释让您感到厌烦™, 相反,让我展示一个基于代码示例的实际示例

    从完成调用线程所有工作的同步版本开始

    class SubjectFactory
    {
        public IEnumerable<Subject> Read(string filePath)
        {
            string[] subjectStrings = File.ReadAllLines(filePath);
    
            return Parse(subjectStrings);
        }
    
        private IEnumerable<Subject> Parse(IEnumerable<string> subjects)
        {
            string code = "XYZ";
    
            foreach ( var subject in subjects )
            {
                yield return new Subject(code, subject);
            }
        }
    }
    
    不幸的是,生活是艰难的,并行编程也是如此。看来你得重新发明轮子了

    private async Task<string[]> ReadAllLinesAsync(string filePath)
    {
        ArrayList allLines = new ArrayList();
    
        using ( var streamReader = new StreamReader(File.OpenRead(filePath)) )
        {
            string line = await streamReader.ReadLineAsync();
    
            allLines.Add(line);
        }
    
        return (string[]) allLines.ToArray(typeof(string));
    }
    
    所有这些就绪后,在WPF应用程序中,您所要做的就是:

    var filePath             = @"X:\subjects\";
    var subjectFactory       = new SubjectFactory();
    var subjectsCollection   = await subjectFactory.ReadAsync(filePath);
    var observableCollection = new ObservableCollection<Subject>(subjectsCollection);
    
    var filePath=@“X:\subjects\”;
    var subjectFactory=新subjectFactory();
    v