2011-09-08 27 views
6

Tôi đang viết một ứng dụng thực hiện tác vụ menu tệp bằng cách sử dụng SwingWorker. Mỗi phương thức được gọi trả về giá trị boolean cho biết liệu hoạt động đã được thực thi thành công hay chưa.Làm cách nào để đọc kết quả của SwingWorker * mà không cần * chờ đợi bận?

Hiện nay tôi đang sử dụng bận rộn chờ đợi kết quả, như thế này:

public boolean executeOperation() { 
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { 
     @Override 
     protected Boolean doInBackground() throws Exception { 
      // .. 

      if (aborted) { 
       return false; 
      } 

      // .. 

      return true; 
     } 
    }; 

    worker.execute(); 

    // busy wait 
    while (!worker.isDone()) 
     ; 

    try { 
     return worker.get().booleanValue(); 
    } catch (Exception e) { 
     // handle exceptions .. 
     return false; 
    } 
} 

Có cách nào bỏ phiếu-dữ dội ít giải quyết này?

Sử dụng worker.get() trực tiếp sẽ không hoạt động, vì nó ngăn chặn các EDT, chờ đợi cho các nhiệm vụ để hoàn thành - có nghĩa là thậm chí các hộp thoại tôi mở từ bên trong SwingWorker sẽ không được sơn.

EDIT: Nếu có thể, tôi muốn tránh rằng phương thức (hoặc công nhân) để truyền đạt kết quả của họ một cách không đồng bộ. Tôi đang thực hiện một số phương thức ngắn (file -> mở, mới, đóng, lưu, lưu, thoát) dựa vào nhau (tức là khi cố gắng thoát, thoát các cuộc gọi gần, đóng có thể gọi là lưu, lưu có thể gọi là lưu như). Giải quyết điều này một cách không đồng bộ dường như làm cho mã phức tạp hơn nhiều.

+0

Bạn luôn có thể đặt trong Thread.sleep (100) trong vòng lặp. –

+2

Điều này không thay đổi thực tế là bạn đang bỏ phiếu cho một kết quả, thay vì có đối tượng thực hiện phép tính cho bạn biết khi nào nó được thực hiện. – mcfinnigan

Trả lời

6

Điểm của SwingWorker chính xác là khởi chạy một số tác vụ trong nền và không chặn EDT. Hoặc bạn muốn một cái gì đó đồng bộ, và EDT sẽ bị chặn bất cứ điều gì bạn cố gắng, hoặc bạn muốn một cái gì đó không đồng bộ, và nhiệm vụ nền nên cập nhật trạng thái của nó bằng cách sử dụng phương pháp publish của SwingWorker.

Bạn có thể hiển thị hộp thoại chế độ chặn với thanh tiến trình trong khi tác vụ đang chạy và ẩn nó sau khi tác vụ hoàn tất.

Cách khác là chặn một thời gian, hy vọng tác vụ sẽ nhanh chóng hoàn thành và sau đó sao lưu sang một cách không đồng bộ. Điều này có thể được thực hiện bằng cách sử dụng đối số get method taking a timeout.

+2

Tôi nghĩ rằng giải quyết vấn đề của tôi: Hiện nay tất cả các phương pháp của tôi sử dụng 'SwingWorker' s - mà có vẻ là một vấn đề, như tôi phải chờ kết quả. Tôi sẽ loại bỏ 'SwingWorker' khỏi các phương thức này và chỉ gọi nó một lần, nhưng thực hiện mọi thứ bên trong' SwingWorker' đồng bộ. – riwi

4

Bạn có thể sử dụng một mô hình không đồng bộ. Nhìn vào Observer/Observable và sử dụng công việc để chuyển kết quả trở lại đối tượng hiện đang tiến hành bỏ phiếu.

+0

Tôi muốn tránh điều đó vì nó sẽ làm phức tạp mã của tôi. Tôi có vài thao tác menu file ngắn (file -> open, new, close, save, save as, exit) dựa vào nhau (tức là khi cố gắng thoát, thoát các cuộc gọi gần, đóng có thể gọi save, save có thể gọi save như). – riwi

+1

đủ công bằng, nó thêm tính phức tạp và nếu bạn đang lập kế hoạch cho các hoạt động chuỗi như vậy nó có thể không lý tưởng. – mcfinnigan

4

Sử dụng worker.get() trực tiếp sẽ không hoạt động, vì nó chặn EDT, chờ nhiệm vụ hoàn thành - nghĩa là ngay cả các hộp thoại tôi mở từ trong SwingWorker sẽ không được sơn.

Chúng không có mã hiện tại. Chờ đợi bận rộn của bạn chặn các EDT nhiều như gọi worker.get() hiện - chỉ có một sự kiện gửi chủ đề, và các hộp thoại trong SwingWorker cũng giống như bị chặn nếu chủ đề đó đang quay trong một vòng lặp hoặc đang chờ một khóa. Vấn đề ở đây là nếu một phương thức chạy trên EDT, nó chỉ đơn giản là không thể trả về một giá trị từ một hoạt động không đồng bộ (mà không cần hogging EDT) cho người gọi của nó.

Cách chính xác để phản ứng với quá trình xử lý không đồng bộ hoàn thành sẽ ghi đè phương pháp done() trong SwingWorker.

Ngoài ra, hãy kiểm tra http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html để biết thêm thông tin.

4

Một cách được đề cập bởi một số người ở trên là ghi đè phương thức thực hiện của SwingWorker. Tuy nhiên, nếu vì một lý do nào đó bạn muốn đăng mã SwingWorker bên ngoài SwingWorker và trong mã gọi, bạn có thể tận dụng sự hỗ trợ thay đổi thuộc tính của SwingWorker.Chỉ cần thêm một PropertyChangeListener vào SwingWorker và lắng nghe thuộc tính trạng thái có một tên thuộc tính là "state". Sau đó bạn có thể trích xuất trạng thái của SwingWorker bằng phương thức getState() của nó. Khi nó được thực hiện, nó sẽ trả về giá trị DONE của hàm SwingWorker.StateValue. Ví dụ (từ một câu trả lời tôi đã đưa ra trong another thread đây trên SO):

if (turn == white) { 
    try { 
     final SwingWorker<Move, Void> mySwingWorker = new SwingWorker<Move, Void>() { 
      @Override 
      protected Move doInBackground() throws Exception { 
       Engine e = new Engine(); // Engine is implemented by runnable 
       e.start(); 
       Move m = e.getBestMove(board);     
       return m; 
      } 
     }; 

     mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() { 
      public void propertyChange(PropertyChangeEvent evt) { 
       if (StateValue.DONE == mySwingWorker.getState()) { 
       try { 
        Move m = mySwingWorker.get(); 

        // TODO: insert code to run on the EDT after move determined 

       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } catch (ExecutionException e) { 
        e.printStackTrace(); 
       } 
       } 
      } 
     }); 
     mySwingWorker.execute(); 

    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 
0

Tôi chạy vào một vấn đề tương tự khi tôi muốn có một hàm trả về một giá trị đó sẽ được tính toán trong một công nhân đu. Tôi không muốn chỉ đơn giản nhận được chủ đề đó để chặn EDT. Tôi cũng không muốn nó chặn. Vì vậy, tôi đã sử dụng một semaphore như thế này:

public boolean executeOperation() { 
    final Semaphore semaphore = new Semaphore(1); 
    semaphore.acquire(1); // surround by try catch... 
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { 
     @Override 
     protected Boolean doInBackground() throws Exception { 
      // .. 

      if (aborted) { 
       semaphore.release(); 
       return false; 
      } 

      // .. 
      semaphore.release(); 
      return true; 
     } 
    }; 

    worker.execute(); 



    try { 
     semaphore.tryAcquire(1, 600, TimeUnit.SECONDS); // awakes when released or when 10 minutes are up. 
     return worker.get().booleanValue(); // blocks here if the task doesn't finish in 10 minutes. 
    } catch (Exception e) { 
     // handle exceptions .. 
     return false; 
    } 
} 

Tôi đoán đây không phải là lý tưởng cho mọi tình huống. Nhưng tôi nghĩ đó là một cách tiếp cận thay thế rất hữu ích cho tôi.

Các vấn đề liên quan