2008-11-14 30 views
9

Tôi đã viết một chương trình đếm các dòng, từ và ký tự trong một văn bản: nó thực hiện điều này với các chuỗi. Đôi khi nó hoạt động rất tốt, nhưng không phải lúc nào cũng tuyệt vời. Điều cuối cùng xảy ra là các biến trỏ đến số từ và ký tự được đếm đôi khi xuất hiện ngắn và đôi khi không.khi nào một chuỗi thoát khỏi phạm vi?

Dường như với tôi rằng các chủ đề đôi khi kết thúc trước khi chúng có thể đếm tất cả các từ hoặc ký tự mà chúng muốn. Có phải vì các chủ đề này nằm ngoài phạm vi khi ngắt vòng lặp while (true) không?

Tôi đã bao gồm mã từ phần nhảy rất yếu của vấn đề của tôi dưới đây:

private void countText() { 
    try { 
    reader = new BufferedReader(new FileReader("this.txt")); 
    while (true) { 
     final String line = reader.readLine(); 
     if(line == null) {break;} 
     lines++; 
     new Thread(new Runnable() {public void run() {chars += characterCounter(line);}}).start(); 
     new Thread(new Runnable() {public void run() {words += wordCounter(line);}}).start(); 
     println(line); 
    } 

    } catch(IOException ex) {return;} 

} 

(Câu hỏi Sub:. Đây là lần đầu tiên tôi đã được hỏi về điều gì đó và gửi mã tôi không muốn để sử dụng StackOverflow thay cho google và wikipedia và tôi lo rằng đây không phải là câu hỏi phù hợp? Tôi đã cố gắng đặt câu hỏi tổng quát hơn để tôi không chỉ yêu cầu trợ giúp về mã của tôi ... mà còn có một câu hỏi khác trang web nơi loại câu hỏi này có thể phù hợp hơn?)

Trả lời

7

Thiết kế có ren khác nhau sẽ giúp việc tìm và khắc phục vấn đề này trở nên dễ dàng hơn và hiệu quả hơn trong việc mặc cả.Đây là một phản ứng dài hơi, nhưng tóm tắt là "nếu bạn đang làm chủ đề trong Java, hãy kiểm tra java.util.concurrent ngay khi có thể)".

Tôi đoán bạn đang đa luồng mã này để tìm hiểu các chủ đề thay vì tăng tốc các từ đếm, nhưng đó là cách rất hiệu quả để sử dụng các chuỗi. Bạn đang tạo hai chủ đề trên mỗi dòng - hai nghìn luồng cho một nghìn tệp dòng. Tạo một luồng (trong các JVM hiện đại) sử dụng tài nguyên hệ điều hành và thường khá đắt. Khi hai - hãy để một mình hai nghìn chủ đề phải truy cập vào một tài nguyên được chia sẻ (chẳng hạn như các số charswords), kết quả của bộ nhớ kết quả cũng làm tổn thương hiệu suất.

Thực hiện các biến số lượt truy cập synchronizedChris Kimpton suggests hoặc Atomic làm WMR suggests có thể sẽ sửa mã, nhưng nó cũng sẽ làm cho hiệu ứng tranh chấp tồi tệ hơn nhiều. Tôi khá chắc chắn nó sẽ đi chậm hơn so với một thuật toán đơn luồng.

Tôi đề xuất chỉ có một chuỗi dài tồn tại sau chars và một cho words, mỗi hàng có một hàng đợi công việc mà bạn gửi công việc mỗi lần bạn muốn thêm số mới. Bằng cách này chỉ có một chủ đề được viết cho mỗi biến, và nếu bạn thay đổi thiết kế, nó sẽ rõ ràng hơn ai chịu trách nhiệm cho những gì. Nó cũng sẽ nhanh hơn vì không có sự tranh cãi về bộ nhớ và bạn không tạo ra hàng trăm luồng trong một vòng lặp chặt chẽ.

Điều này cũng quan trọng, khi bạn đã đọc tất cả các dòng trong tệp, để chờ cho tất cả các chuỗi kết thúc trước khi bạn thực sự in ra các giá trị của bộ đếm, nếu không bạn sẽ mất các cập nhật từ chủ đề chưa xong. Với thiết kế hiện tại của bạn, bạn sẽ phải xây dựng một danh sách lớn các chủ đề mà bạn đã tạo, và chạy qua nó ở cuối kiểm tra xem chúng đã chết chưa. Với một thiết kế thread-and-worker-thread, bạn chỉ có thể yêu cầu mỗi thread thoát khỏi hàng đợi của nó và sau đó đợi cho đến khi nó hoàn tất.

Java (từ 1,5 trở lên) làm cho loại thiết kế này rất dễ triển khai: kiểm tra java.util.concurrent.Executors.newSingleThreadExecutor. Nó cũng làm cho nó dễ dàng để thêm đồng thời nhiều hơn sau này (giả sử khóa thích hợp vv), như bạn chỉ có thể chuyển sang một hồ bơi thread hơn là một sợi duy nhất.

+0

Tôi chưa đợi các chủ đề hoàn thành. Bạn đang phải, tôi chỉ làm điều này để có được một hang của các phương pháp tôi sẽ sử dụng với các chủ đề: nhiệm vụ không yêu cầu chủ đề ở tất cả. Làm thế nào để bạn đợi một chuỗi kết thúc? Tôi có thể đợi Thread.activeCount() trả lại một số nhỏ không? – Ziggy

+0

Thread.join() chờ một sợi đơn chết. Đợi đếm số đếm bằng 1 có thể hoạt động - tôi nghi ngờ bạn có thể chạy vào các điều kiện chủng tộc với các chủ đề đang trong quá trình khởi động, nhưng tôi không chắc chắn. –

+0

Nếu bạn muốn nhận được hang của chủ đề, tôi khuyên bạn nên nhìn vào các công trình/thread hồ bơi/làm việc hàng đợi cách làm việc. Một khi bạn nhận được đầu của bạn xung quanh nó, nó thực sự dễ dàng hơn nhiều để lý do về việc tạo ra các chủ đề bằng tay. –

3

Nghe như một câu hỏi hay với tôi ... Tôi nghĩ rằng vấn đề có thể liên quan đến nguyên tử y của ký tự + = và từ + = - một số chuỗi có thể gọi cùng một lúc - bạn có làm gì để đảm bảo rằng không có sự xen kẽ nào.

Đó là:

Chủ đề 1, có chars = 10, muốn thêm 5

Chủ đề 2, có chars = 10, muốn thêm 3

Chủ đề 1 hoạt động ra tổng mới, 15

chủ đề 2 hoạt động ra tổng mới, 13

chủ đề 1 bộ chars đến 15

Chủ đề 2 đặt ký tự thành 13.

Có thể thực hiện được trừ khi bạn sử dụng đồng bộ hóa khi cập nhật các vars đó.

+1

Aha! Bạn thấy đấy, tôi hoàn toàn học về sự xen kẽ và nguyên tử và đồng bộ và khóa, nhưng điều đó vẫn chưa bao giờ xảy ra với tôi. Đó là chính xác vấn đề, không có nghi ngờ! – Ziggy

+1

Hmm ... Tôi đã sử dụng đồng bộ hóa (điều này) {xung quanh + = nội dung} nhưng vẫn nhận được kết quả không thể đoán trước ... – Ziggy

+0

ồ người đàn ông, tôi không nghĩ vậy. Tôi đã thêm một println (Thread.activeCount()); điều đó sẽ cho tôi cảm giác về những gì đang diễn ra. Dường như đôi khi tôi chỉ nhận được 12 chuỗi đầy đủ hoạt động trước khi kết thúc vòng lặp while. Đó là vấn đề: không đủ thời gian! – Ziggy

4

Vì Chris Kimpton đã chỉ ra chính xác bạn có vấn đề với việc cập nhật charswords trong các chủ đề khác nhau. Đồng bộ hóa trên this sẽ không hoạt động vì this là tham chiếu đến chuỗi hiện tại có nghĩa là các luồng khác nhau sẽ đồng bộ hóa trên các đối tượng khác nhau. Bạn có thể sử dụng thêm một "đối tượng khóa" bạn có thể đồng bộ hóa trên nhưng cách đơn giản nhất để khắc phục điều này có lẽ sẽ được sử dụng AtomicIntegers trong 2 quầy:

AtomicInteger chars = new AtomicInteger(); 
... 
new Thread(new Runnable() {public void run() { chars.addAndGet(characterCounter(line));}}).start(); 
... 

Trong khi điều này có thể sẽ khắc phục vấn đề của bạn, Sam Stoke's more detailed answer là hoàn toàn đúng , thiết kế ban đầu là rất kém hiệu quả.

Để trả lời câu hỏi của bạn về thời điểm một chuỗi "nằm ngoài phạm vi": Bạn đang bắt đầu hai chuỗi mới cho mỗi dòng trong tệp của mình và tất cả chúng sẽ chạy cho đến khi kết thúc phương thức run() của chúng. Điều này là trừ khi bạn làm cho chúng daemon threads), trong trường hợp đó, chúng sẽ thoát ngay sau khi các chuỗi daemon là những người duy nhất vẫn chạy trong JVM này.

+0

Tôi đã triển khai AtomicIntegers và tăng tỷ lệ thành công của tôi. Hiện vẫn còn chạy trong đó cả hai số lượng thấp hơn họ phải là ... – Ziggy

+0

Có thể bạn không phải chờ đợi cho tất cả các chủ đề để hoàn thành trước khi in kết quả. Xem câu trả lời của tôi dưới đây. –

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