まず、Go言語の方のやつを、書き出してみる。
package main import ( "time" "math/rand" "fmt") var ( Web1 = fakeSearch("web1") Image1 = fakeSearch("image1") Video1 = fakeSearch("video1") Web2 = fakeSearch("web2") Image2 = fakeSearch("image2") Video2 = fakeSearch("video2") ) type Result string type Search func(query string) Result func fakeSearch(kind string) Search { return func(query string) Result { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) return Result(fmt.Sprintf("%s result for %q\n", kind, query)) } } func First(query string, replicas ...Search) Result { c := make(chan Result) searchReplica := func(i int) { c <- replicas[i](query) } for i := range replicas { go searchReplica(i) } return <-c } func Google(query string) (results []Result) { c := make(chan Result) go func() { c <- First(query, Web1, Web2) } () go func() { c <- First(query, Image1, Image2) } () go func() { c <- First(query, Video1, Video2) } () timeout := time.After(80 * time.Millisecond) for i := 0; i < 3; i++ { select { case result := <-c: results = append(results, result) case <-timeout: fmt.Println("timed out") return } } return} func main() { rand.Seed(time.Now().UnixNano()) start := time.Now() result := First("golang", fakeSearch("replica 1"), fakeSearch("replica 2")) elapsed := time.Since(start) fmt.Println(result) fmt.Println(elapsed) }
コードの解説はスライドに譲るとして、First関数の中での、return <-c の使い方が素晴らしい。
で、これをJavaで書いてみた。
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; class Web1 implements Callable<String>{ public String call() { try { Thread.sleep(10*1000); }catch (Exception e){ } return "Web1"; } } class Web2 implements Callable<String>{ public String call() { return "Web2"; } } class Image1 implements Callable<String>{ public String call() { try { Thread.sleep(10*1000); }catch (Exception e){ } return "Image1"; } } class Image2 implements Callable<String>{ public String call() { return "Image2"; } } class Video1 implements Callable<String>{ public String call() { try { Thread.sleep(10*1000); }catch (Exception e){ } return "Video1"; } } class Video2 implements Callable<String>{ public String call() { return "Video2"; } } public class CmpGoChannel { public static void First(ExecutorService executor1,Callable<String> callable1, Callable<String> callable2) throws Exception{ List<Future<String>> list = new ArrayList<>(); list.add(executor1.submit(callable1)); list.add(executor1.submit(callable2)); boolean loopDone = false; while(true) { for (Future<String> f : list) { System.out.print("."); if (f.isDone()) { System.out.println(f.get()); loopDone = true; break; } } if(loopDone){ break; } } } public static void main(String... args) throws Exception{ ExecutorService executor1 = Executors.newFixedThreadPool(2); ExecutorService executor2 = Executors.newFixedThreadPool(2); ExecutorService executor3 = Executors.newFixedThreadPool(2); CmpGoChannel c = new CmpGoChannel(); List<Future<String>> list = new ArrayList<>(); Callable<String> web1 = new Web1(); Callable<String> web2 = new Web2(); First(executor1,web1,web2); Callable<String> image1 = new Image1(); Callable<String> image2 = new Image2(); First(executor2,image1,image2); Callable<String> video1 = new Video1(); Callable<String> video2 = new Video2(); First(executor3,video1,video2); executor1.shutdown(); executor2.shutdown(); executor3.shutdown(); } }
まず気づいたのが、Javaにchannelに相当する仕組みがなかったこと。JavaのConcurrencyはそんなに人気のあるものではなかったが、そうは言ってもそれなりに使われている実績はあるので、書こうとしてみて「そういや、なかったな」みたいな気付きがあった。
なので、ここは
while(true) { for (Future<String> f : list) { System.out.print("."); if (f.isDone()) { System.out.println(f.get()); loopDone = true; break; } } if(loopDone){ break; } }
このあたりですでに、簡素なコードを書けるというGoの魅了が十分に表れている。
0 件のコメント:
コメントを投稿