6

特定のフォルダー内のファイルを並行して処理する必要があるとします。Java ではFolderReader、フォルダーとスレッドのプールからファイル名を読み取るスレッドを作成しますFileProcessorFolderReaderファイル名を読み取り、ファイル処理関数 ( Runnable) をプール エグゼキュータに送信します。

Scala には、次の 2 つのオプションがあります。

  • アクターのプールを作成し、FileProcessorでファイル処理機能をスケジュールしますActors.Scheduler
  • ファイル名を読み取りながら、ファイル名ごとにアクターを作成します。

それは理にかなっていますか?最良の選択肢は何ですか?

4

6 に答える 6

10

あなたがしていることに応じて、それは次のように簡単かもしれません

for(file<-files.par){
   //process the file
}
于 2012-07-20T12:36:55.983 に答える
3

全力を尽くして、スレッドからできるだけ離れることをお勧めします。幸いなことに、以下で起こっていることを処理するより優れた抽象化があります。あなたの場合、アクターを使用する必要はないように見えますが (できる限り)、Futures と呼ばれるより単純な抽象化を使用できます。それらは Akka オープン ソース ライブラリの一部であり、将来的には Scala 標準ライブラリの一部にもなると思います。

Future[T] は単に未来に T を返すものです。

フューチャを実行するために必要なのは、Java executor サービスから派生できる暗黙の ExecutionContext を持つことだけです。そうすれば、エレガントな API と、コレクションをフューチャーのコレクションに変換し、結果を収集するためのフューチャーがモナドであるという事実を楽しむことができます。http://doc.akka.io/docs/akka/2.0.1/scala/futures.htmlをご覧になることをお勧めします。

object TestingFutures {
  implicit val executorService = Executors.newFixedThreadPool(20)
  implicit val executorContext = ExecutionContext.fromExecutorService(executorService)

  def testFutures(myList:List[String]):List[String]= {

    val listOfFutures : Future[List[String]] = Future.traverse(myList){
      aString => Future{
                        aString.reverse
                       }
     }
    val result:List[String] = Await.result(listOfFutures,1 minute)
    result

  }
}

ここで多くのことが起こっています:

  • 私はFuture.traversewhich receive を最初のパラメーターとして 使用M[T]<:Traversable[T]し、2 番目のパラメーターとして a を使用するT => Future[T]か、a を好む場合Function1[T,Future[T]]は Future[M[T]] を返します
  • メソッドを使用してFuture.apply、タイプの匿名クラスを作成していますFuture[T]

Akka の先物を検討する理由は他にもたくさんあります。

  • Future はモナドであるため、マッピングできます。つまり、Future の実行を連鎖させることができます。

    Future { 3 }.map { _ * 2 }.map { _.toString }

  • Future にはコールバックがあります: future.onComplete、onSuccess、onFailure、andThen など。

  • 先物はトラバースだけでなく、理解もサポートします

于 2012-07-20T09:49:34.530 に答える
2

@Edmondo1984が私を打ち負かしたことを除いて、私は正確に何をしたかを書くつもりでした。:) 私は彼の提案を大々的に支持します。また、 Akka 2.0.2のドキュメントを読むことをお勧めします。同様に、もう少し具体的な例を挙げます。

import akka.dispatch.{ExecutionContext, Future, Await}
import akka.util.duration._
import java.util.concurrent.Executors
import java.io.File

val execService = Executors.newCachedThreadPool()
implicit val execContext = ExecutionContext.fromExecutorService(execService)

val tmp = new File("/tmp/")
val files = tmp.listFiles()
val workers = files.map { f =>
  Future {
    f.getAbsolutePath()
  }
}.toSeq
val result = Future.sequence(workers)
result.onSuccess {
  case filenames =>
    filenames.foreach { fn =>
      println(fn)
    }
}

// Artificial just to make things work for the example
Thread.sleep(100)
execContext.shutdown()

ここではsequenceの代わりにを使用traverseしていますが、違いはニーズによって異なります。

友よ、未来と共に行こう。この場合、アクターはより苦痛なアプローチです。


于 2012-07-20T10:00:50.943 に答える
2

しかし、アクターを使用する場合、何が問題なのですか?

プロパティファイルの読み取り/書き込みが必要な場合。私のJavaの例があります。しかし、それでも Akka Actors と一緒です。

アクターActorFileが 1 つのファイルを表すとしましょう。うーん.. おそらくそれは 1 つのファイルを表すことはできません。右?(できればいいですね)。したがって、次のようないくつかのファイルを表しますPropertyFilesActor

なぜこのようなものを使用しないでください:

public class PropertyFilesActor extends UntypedActor {

    Map<String, String> filesContent = new LinkedHashMap<String, String>();

    { // here we should use real files of cource
        filesContent.put("file1.xml", "");
        filesContent.put("file2.xml", "");
    }

    @Override
    public void onReceive(Object message) throws Exception {

        if (message instanceof WriteMessage)  {
            WriteMessage writeMessage = (WriteMessage) message;
            String content = filesContent.get(writeMessage.fileName);
            String newContent = content + writeMessage.stringToWrite;
            filesContent.put(writeMessage.fileName, newContent);
        }

        else if (message instanceof ReadMessage) {
            ReadMessage readMessage = (ReadMessage) message;
            String currentContent = filesContent.get(readMessage.fileName);
            // Send the current content back to the sender
            getSender().tell(new ReadMessage(readMessage.fileName, currentContent), getSelf());
        }

        else unhandled(message);

    }

}

...メッセージはパラメーター (fileName) で送信されます

独自の があり、次のin-boxようなメッセージを受け入れます。

  1. WriteLine(ファイル名, 文字列)
  2. ReadLine(ファイル名, 文字列)

in-boxこれらのメッセージは、次々と順番に に格納されます。アクターは、ボックスからメッセージを受信することで作業を行います。つまり、保存/読み取りを行い、その間にフィードバックsender ! messageを送り返します。

したがって、プロパティ ファイルに書き込み、Web ページのコンテンツを表示して送信するとします。ページの表示を開始し (データをファイルに保存するメッセージを送信した直後)、フィードバックを受け取るとすぐに、更新されたファイルのデータでページの一部を更新します (ajax による)。

于 2013-05-15T20:12:04.033 に答える
2

理想的には、2 つのアクターを使用する必要があります。1 つはファイルのリストを読み取るためのもので、もう 1 つは実際にファイルを読み取るためのものです。

単一の「開始」メッセージを最初のアクターに送信するだけでプロセスを開始します。その後、アクターはファイルのリストを読み取り、2 番目のアクターにメッセージを送信できます。次に、2 番目のアクターがファイルを読み取り、コンテンツを処理します。

複数のアクターを持つことは、複雑に見えるかもしれませんが、理論上のオブジェクト指向システムのように、互いに通信する多数のオブジェクトがあるという意味では、実際には良いことです。

編集:単一のファイルの同時読み取りを行うべきではありません。

于 2012-07-20T09:37:55.863 に答える
1

さて、ファイルを取得して、並列構造に貼り付けます

scala> new java.io.File("/tmp").listFiles.par
res0: scala.collection.parallel.mutable.ParArray[java.io.File] = ParArray( ... )

それで...

scala> res0 map (_.length)
res1: scala.collection.parallel.mutable.ParArray[Long] = ParArray(4943, 1960, 4208, 103266, 363 ... )
于 2012-07-20T16:12:28.357 に答える