2012-05-12 11 views
9

Tôi đã cố gắng sao chép ví dụ trên Scala mới 2.10 futures feature. Mã Tôi đã sử dụng là:Lỗi có thể xảy ra trong Scala 2.10: Tương lai không chạy

import scala.concurrent.Future 
import scala.concurrent.future 

object Test { 
    def main(args: Array[String]) { 
    println("Test print before future") 
    val s = "Hello" 
    val f = future {s + " future!"} 
    f onSuccess {case v => println(v)} 
    println("Test print after future") 
    } 
} 

Thay vì in ấn:

Test print before future 
Hello future! 
Test print after future 

Nó chỉ đơn giản in:

Test print before future 
Test print after future 

Bất kỳ ý tưởng về lý do tại sao tôi có hành vi này? Phiên bản trình biên dịch scala của tôi là 2.10.0-20120507.

Trả lời

28

Vấn đề là bạn đang thực hiện điều đó như một chương trình độc lập, mà chính thread là chấm dứt trước khi một trong các chủ đề công nhân có thể thực thi "Hello future!" println. (Các chủ đề mà thư viện tương lai mới sinh ra là các chuỗi daemon).

Bạn cũng có thể sử dụng đối tượng Await (cũng trong scala.concurrent) đợi cho đến khi tương lai f được hoàn thành:

import scala.concurrent._ 
import scala.concurrent.util._ 

object Test { 
    def main(args: Array[String]) { 
    println("Test print before future") 

    val s = "Hello" 
    val f = future {s + " future!"} 
    f onSuccess {case v => println(v)} 
    println("Test print after future") 

    Await.ready(f, Duration.Inf) 
    } 
} 

này có thể in:

Test print before future 
Test print after future 
Hello future! 

Hoặc, nó có thể in "Hello Tương lai!" trước khi "Thử in sau tương lai" tùy thuộc vào lịch trình của chuỗi.

Tương tự như vậy, bạn có thể buộc các chủ đề chính để chờ cho đến khi f được hoàn thành trước cuối println như sau:

import scala.concurrent._ 
import scala.concurrent.util._ 

object Test { 
    def main(args: Array[String]) { 
    println("Test print before future") 

    val s = "Hello" 
    val f = future {s + " future!"} 
    f onSuccess {case v => println(v)} 

    Await.ready(f, Duration.Inf)   

    println("Test print after future") 
    } 
} 

Trong đó sẽ in:

Test print before future 
Hello future! 
Test print after future 

Tuy nhiên, lưu ý rằng khi bạn sử dụng Await, bạn đang chặn. Điều này tất nhiên có ý nghĩa để đảm bảo rằng chuỗi ứng dụng chính của bạn không chấm dứt, nhưng nói chung không nên được sử dụng trừ khi cần thiết khác.

(đối tượng Await là một lối thoát cần thiết cho các tình huống như thế này, nhưng việc sử dụng nó trong suốt mã ứng dụng mà không cần quan tâm đến ngữ nghĩa của nó có thể dẫn đến thực thi chậm hơn, ít song song hơn. theo thứ tự được chỉ định, ví dụ, có các phương án thay thế khác, chẳng hạn như các phương pháp andThenmap trên Future.)

+0

Bạn biết không? Tôi nghĩ về nguyên nhân này là chính xác theo cách tương tự khi bạn xử lý goroutines: bạn sử dụng các kênh để chờ một tin nhắn được gửi bởi goroutines trước khi đóng khối chính. –

+0

@Heather Có thể chờ đợi nhiều hơn một tương lai không? – 66CLSjY

1

Tôi nghĩ rằng vấn đề ở đây là thời gian. Có lẽ mã tương lai của bạn đang chạy trong chuỗi deamon riêng biệt. Tôi nghĩ rằng ứng dụng kết thúc rất nhanh và chuỗi deamon này không có đủ thời gian để thực thi đúng cách (ứng dụng không đợi cho các chuỗi deamon kết thúc). Nhưng điều này cũng rất phụ thuộc vào hệ thống. Đối với tôi, bản in:

Test print before future 
Test print after future 
Hello future! 

và sau đó thoát (tôi đang sử dụng Scala 2.10.0-M3). Bạn có thể thử sau đây để kiểm tra nó - chỉ cần đặt chủ đề thực hiện chính trong giấc ngủ trong vài giây và xem liệu Hello future! được in:

import scala.concurrent.Future 
import scala.concurrent.future 

object Test { 
    def main(args: Array[String]) { 
     println("Test print before future") 

     val s = "Hello" 
     val f = future {s + " future!"} 
     f onSuccess {case v => println(v)} 

     println("Test print after future") 

     Thread.sleep(3000) 
     println("Test print at the end.") 
    } 
} 
1

Tôi chỉ muốn thêm rằng nói chung có khả năng khác là tương lai không chạy: Nhấn giới hạn của luồng chủ đề.

Trong trường hợp của bạn thì có lẽ nó chỉ đơn giản là một vấn đề thời gian như những người khác đã chỉ ra, nhưng khi tham khảo sau này xem xét ví dụ sau:

import scala.concurrent._ 
import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.duration.Duration 


object FutureDebug { 
    def main(args: Array[String]) { 

    for (i <- Range(0, 4)) { 
     future { 
     while (true) { 
      Thread.sleep(1000) 
      println("I'm doing stupid things in a future") 
     } 
     } 
    } 

    println("(1) reached? yes") 
    val fut = future { 
     for (i <- Range(0, 1000)) { 
     println("never reached " + i) 
     } 
     3.14 
    }  
    println("(2) reached? yes") 
    Await.result(fut, Duration.Inf) 
    println("(3) reached? no") 
    } 
} 

Trên máy tính của tôi mặc định bối cảnh thực hiện toàn cầu chỉ có 4 Chủ đề. Vì các luồng công nhân đang bận rộn thực hiện 4 tương lai không có ý nghĩa, tương lai bên dưới sẽ không bao giờ chạy. Đây là lý do tại sao người ta phải cẩn thận với bối cảnh thực thi mặc định và tốt nhất là specify one's own execution context khi giao dịch với nhiều tương lai chạy dài (thực sự).