0

サイズの大きいファイルがたくさんあります。それらはすべて解析する必要があり、非常に長い時間がかかります。そこで、私はアイデアを思いつきました。一方のスレッドがファイルを読み取る間(ここではハードディスクの読み取り速度がボトルネックです)、もう一方のスレッドは行から必要な情報を解析する必要があり、解析が行われている間、ファイル読み取りスレッドは次のファイルなど。

2つのスレッドを作成するだけで、1つはFile.ReadAllLinesによってすべてのファイルを読み取り、もう1つは返された配列を解析します。ただし、これは非常に多くのメモリを消費します。したがって、たとえば、読み取るファイルの数を5に制限する必要があります。

私が抱えているもう1つの問題は、ライン取得プロセスが完了するのを待っていることです。解析スレッドは、配列を解析する準備ができているかどうかを知る必要があります。

問題は、私はどのように従うべきかということです。その例はありますか(見つかりませんでした)?それとももっと良いアイデアはありますか?

4

3 に答える 3

1

解析が常に読み取りよりも高速であることが確実な場合は、非常に簡単に行うことができます。スレッドA(UIスレッドをブロックしないスレッド)がファイルを読み取り、新しいスレッドBを開始して、ファイルの内容をに渡します。それ(スレッドの代わりにタスクを使用すると簡単になります)。それをループに入れれば完了です。解析が高速であるため、スレッドAが新しいスレッドを開始する前に2番目のスレッド/タスクが終了します。したがって、同時に実行されるスレッドは2つだけで、メモリ内のファイルは2つだけです。

回線取得プロセスが完了するのを待っています。解析スレッドは、配列を解析する準備ができているかどうかを知る必要があります。

正しく理解できているかわかりませんが、上記の「解決策」で解決できます。常に新しいスレッド/タスクを開始するため、ファイルが完全に読み取られた場合にのみ。

更新:処理が(常に)読み取りよりも高速でない場合は、たとえば次のように実行できます。

Private MaxTasks As Integer = 4

Private Async Sub ReadAndProcess(ByVal FileList As List(Of String))

    Dim ProcessTasks As New List(Of Task)

    For Each fi In FileList
        Dim tmp = fi
        Console.WriteLine("Reading {0}", tmp)
        Dim FileContent = Await Task.Run(Of Byte())(Function() As Byte()
                                                        Return File.ReadAllBytes(tmp)
                                                    End Function)
        If ProcessTasks.Count >= MaxTasks Then
            Console.WriteLine("I have to wait!")
            Dim NextReady = Await Task.WhenAny(ProcessTasks)
            ProcessTasks.Remove(NextReady)
        End If

        Console.WriteLine("I can start a new process-task!")
        ProcessTasks.Add(Task.Run(Sub()
                                      Console.WriteLine("Processing {0}", tmp)
                                      Dim l As Long
                                      For Each b In FileContent
                                          l += b
                                      Next
                                      System.Threading.Thread.Sleep(2000)
                                      Console.WriteLine("Done with {0}", tmp)
                                  End Sub))
    Next

    Await Task.WhenAll(ProcessTasks)

End Sub

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim ofd As New OpenFileDialog
    ofd.Multiselect = True
    If ofd.ShowDialog = Windows.Forms.DialogResult.OK AndAlso ofd.FileNames.Count >= 1 Then
        ReadAndProcess(ofd.FileNames.ToList)
    End If

End Sub

アイデア(通常、.Netでは4ダースの方法で実装できます)は、自分で設定した制限に達するまで、新しい処理タスクをスケジュールするというものです。それが達成された場合は、タスクの準備ができるまで「待機」し、新しいタスクを開始します。

UPDATE2:TPL libを使用すると、次のようになります。

Private Sub Doit()

    Dim ABProcess As New ActionBlock(Of Tuple(Of String, Byte()))(Sub(tp)
                                                                      Console.WriteLine("Processing {0}", tp.Item1)
                                                                      Dim l As Long
                                                                      For Each el In tp.Item2
                                                                          l += el
                                                                      Next
                                                                      System.Threading.Thread.Sleep(1000)
                                                                      Console.WriteLine("Done with {0}", tp.Item1)
                                                                  End Sub, New ExecutionDataflowBlockOptions With {.MaxDegreeOfParallelism = 4, .BoundedCapacity = 4})

    Dim ABRead As New ActionBlock(Of String())(Async Sub(sarr)
                                                   For Each s In sarr
                                                       Console.WriteLine("Reading {0}", s)
                                                       Dim t = New Tuple(Of String, Byte())(s, File.ReadAllBytes(s))
                                                       Dim taken = Await ABProcess.SendAsync(t)
                                                       Console.WriteLine("Output taken = {0}", taken)
                                                   Next
                                                   Console.WriteLine("All reading done")
                                               End Sub)

    Dim ofd As New OpenFileDialog
    ofd.Multiselect = True
    If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
        ABRead.Post(ofd.FileNames)
    End If

End Sub

どのバージョンが「より良い」か...個人的な好みかもしれません;)新しいTPLブロックは時々非常にブラックボックスっぽいので、個人的には「手動」バージョンを好むかもしれません。

于 2012-12-11T12:44:03.897 に答える
1

私は非常によく似たアプリケーションを持っており、BlockingCollectionを使用しています

BlockingCollectionの概要

私の場合、解析は読み取りよりも高速ですが、ファイルが同じサイズではないため、読み取りが解析を待機している可能性があるという問題がありました。
BlockingCollectionを使用すると、キューサイズ8でサイズの差異を管理し、メモリを抑制しました。
解析が遅い場合は、解析を並列に設定することもできます。
単一のヘッドから読み取る場合、並列読み取りは役に立ちません。

static void Main(string[] args)
{
    // A blocking collection that can hold no more than 5 items at a time.
    BlockingCollection<string[]> fileCollection = new BlockingCollection<string[]>(5);

    // Start one producer and one consumer.
    Task.Factory.StartNew(() => NonBlockingConsumer(fileCollection));  // parse - can use parallel
    Task.Factory.StartNew(() => NonBlockingProducer(fileCollection));  // read
}

解析の性質は何ですか?
一度に1行ずつ解析していますか?
ファイルの並列解析の前に、行の並列解析を調べます。
Parallel.ForEachメソッド

于 2012-12-11T14:29:42.987 に答える
0

必要処理に応じて、タスクを分割することでパフォーマンスが向上する場合があります。プロセス全体は、プロデューサー/コンシューマーのセットアップと同じように聞こえます。

処理をキューに入れるために、ブロッキングキューを確認することをお勧めします。アイデアは、読み取りスレッドのキュー項目と、項目をデキューして処理するための処理スレッドを持つことです。

于 2012-12-11T12:19:51.677 に答える