Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C#控制台应用程序中使用async和Wait异步处理文件列表_C#_Multithreading_Asynchronous_Async Await - Fatal编程技术网

在C#控制台应用程序中使用async和Wait异步处理文件列表

在C#控制台应用程序中使用async和Wait异步处理文件列表,c#,multithreading,asynchronous,async-await,C#,Multithreading,Asynchronous,Async Await,我正在一个简单的控制台应用程序中使用C语言中的async和wait。我的目标很简单:以异步方式处理文件列表,以便一个文件的处理不会阻止其他文件的处理。没有一个文件是相互依赖的,并且(比方说)有数千个文件需要处理 这是我目前拥有的代码 public class MyClass { public void Go() { string[] fileSystemEntries = Directory.GetFileSystemEntries(@"Path\To\Files

我正在一个简单的控制台应用程序中使用C语言中的
async
wait
。我的目标很简单:以异步方式处理文件列表,以便一个文件的处理不会阻止其他文件的处理。没有一个文件是相互依赖的,并且(比方说)有数千个文件需要处理

这是我目前拥有的代码

public class MyClass
{
    public void Go()
    {
        string[] fileSystemEntries = Directory.GetFileSystemEntries(@"Path\To\Files");

        Console.WriteLine("Starting to read from files!");
        foreach (var filePath in fileSystemEntries.OrderBy(s => s))
        {
            Task task = new Task(() => DoStuff(filePath));
            task.Start();
            task.Wait();
        }
    }

    private async void DoStuff(string filePath)
    {
        await Task.Run(() =>
        {
            Thread.Sleep(1000);
            string fileName = Path.GetFileName(filePath);
            string firstLineOfFile = File.ReadLines(filePath).First();
            Console.WriteLine("{0}: {1}", fileName, firstLineOfFile);
        });
    }
}
我的
Main()
方法只调用这个类:

public static class Program
{
    public static void Main()
    {
        var myClass = new MyClass();
        myClass.Go();
    }
}
这种异步编程模式中有一部分我似乎遗漏了,因为每当我运行程序时,实际处理的文件数量似乎是随机的,从没有到全部六个(在我的示例文件集中)


基本上,主线程不会等待处理所有文件,我想这是异步运行的一部分,但我不太希望这样。我想要的是:以尽可能多的线程处理尽可能多的这些文件,但仍要等待它们全部完成处理,然后再结束。

我将上面的注释组合在一起,以得出我的解决方案。实际上,我根本不需要使用
async
wait
关键字。我只需要创建一个任务列表,启动所有任务,然后调用WaitAll。不需要用
async
wait
关键字修饰任何内容。以下是生成的代码:

public class MyClass
{
    private int filesRead = 0;

    public void Go()
    {
        string[] fileSystemEntries = Directory.GetFileSystemEntries(@"Path\To\Files");

        Console.WriteLine("Starting to read from files! Count: {0}", fileSystemEntries.Length);
        List<Task> tasks = new List<Task>();
        foreach (var filePath in fileSystemEntries.OrderBy(s => s))
        {
            Task task = Task.Run(() => DoStuff(filePath));
            tasks.Add(task);
        }
        Task.WaitAll(tasks.ToArray());
        Console.WriteLine("Finish! Read {0} file(s).", filesRead);
    }

    private void DoStuff(string filePath)
    {
        string fileName = Path.GetFileName(filePath);
        string firstLineOfFile = File.ReadLines(filePath).First();
        Console.WriteLine("[{0}] {1}: {2}", Thread.CurrentThread.ManagedThreadId, fileName, firstLineOfFile);
        filesRead++;
    }
}

现在似乎有很多方法可以在C#中实现异步编程。在
并行
任务
异步
/
等待
之间,有很多选择。基于此线程,对我来说最好的解决方案似乎是
并行
,因为它提供了最干净的解决方案,比手动创建
任务
对象效率更高,并且在获得类似结果的同时,不会使用
async
await
关键字将代码弄乱。

异步/await背后的主要设计目标之一是促进自然异步I/O API的使用。因此,您的代码可能会这样重写(未经测试):


注意,它不会显式生成任何新线程,但这可能发生在使用
wait reader.readlinesync().configurewait(false)

启动并等待foreach循环中的每个任务的幕后。。。创建一个任务数组并使用WaitAll。此代码在概念上存在一些问题,但主要的技术问题是:
新任务(()=>DoStuff(filePath))
,其中
DoStuff
是一种
异步void
方法。您正在执行一个fire and forget调用,任务在
DoStuff
方法完成之前完成,
myClass.Go()
也是如此。并行处理项是一个非常基本的并发主题。事实上,据我所知,你的问题是关于最基本的情况。如果你在这个方向上做些研究可能是最好的。您会很快找到解决方案。@Scott是的,自从引入了async/await之后,概念变得模棱两可,对初学者来说会产生误导。我看到async/await一直在使用,而同步线程本来会更简单,完成同样的事情。这个问题现在已经足够清楚,可以与现有的材料共存。如果你喜欢的话,把你现有的答案加上。重点是,这个问题对未来的访问者更有用。接受你认为对他人最有帮助的答案。提示:使用
任务。运行
而不是
新任务
+
任务。开始
。谢谢!更新的答案反映了这一点。
Task.Run
是错误的方法。使用
Task.Run
进行长时间运行、CPU受限的操作。对于长时间运行的I/O绑定操作,请使用
async/await
。Noseratio的答案是这里要采取的正确方法。我实际上认为,
Parallel.ForEach
现在最符合我的目的了。不过,你是对的:我认为这个任务不是我应该使用的。
public class MyClass
{
    private int filesRead;

    public void Go()
    {
        string[] fileSystemEntries = Directory.GetFileSystemEntries(@"Path\To\Files");

        Console.WriteLine("Starting to read from files! Count: {0}", fileSystemEntries.Length);
        Parallel.ForEach(fileSystemEntries, DoStuff);
        Console.WriteLine("Finish! Read {0} file(s).", filesRead);
    }

    private void DoStuff(string filePath)
    {
        string fileName = Path.GetFileName(filePath);
        string firstLineOfFile = File.ReadLines(filePath).First();
        Console.WriteLine("[{0}] {1}: {2}", Thread.CurrentThread.ManagedThreadId, fileName, firstLineOfFile);
        filesRead++;
    }
}
public class MyClass
{
    private int filesRead = 0;

    public void Go()
    {
        GoAsync().Wait();
    }

    private async Task GoAsync()
    {
        string[] fileSystemEntries = Directory.GetFileSystemEntries(@"Path\To\Files");

        Console.WriteLine("Starting to read from files! Count: {0}", fileSystemEntries.Length);

        var tasks = fileSystemEntries.OrderBy(s => s).Select(
            fileName => DoStuffAsync(fileName));
        await Task.WhenAll(tasks.ToArray());

        Console.WriteLine("Finish! Read {0} file(s).", filesRead);
    }

    private async Task DoStuffAsync(string filePath)
    {
        string fileName = Path.GetFileName(filePath);
        using (var reader = new StreamReader(filePath))
        {
            string firstLineOfFile = 
                await reader.ReadLineAsync().ConfigureAwait(false);
            Console.WriteLine("[{0}] {1}: {2}", Thread.CurrentThread.ManagedThreadId, fileName, firstLineOfFile);
            Interlocked.Increment(ref filesRead);
        }
    }
}