2012-03-30 25 views
8

My Java NIO Selector được thực hiện sử dụng select() nên khối cho đến khi bất kỳ những xảy ra:Java NIO Selector select() trả về 0 mặc dù các kênh truyền hình đã sẵn sàng

  1. một kênh đăng ký sẵn sàng
  2. nó là wakeup() ' ed
  3. thread bị gián đoạn

Từ đó, tôi đã thực hiện một vài giả thuyết về trường hợp select() trả về 0:

  • nó hẳn phải là lý do 2. hay 3.
  • selectedKeys() nên trả về một sản phẩm nào ResultSet
  • Tôi không cần phải gọi selectedKeys() và có thể tiếp tục lặp vòng lặp kế tiếp, nơi select() sẽ được gọi một lần nữa

Tuy nhiên, tôi gặp phải các tình huống trong đó select() trả về 0 mặc dù có kênh sẵn sàng. selectedKeys() trả về một Set với 1 SelectionKey như mong đợi.

Thậm chí nhiều cuộc gọi tới select() sẽ luôn trả về 0 cho đến khi kênh được xử lý và SelectionKey đã bị xóa. Tình trạng này về cơ bản kết thúc trong một vòng lặp vô tận như select() không chặn nhưng luôn luôn ngay lập tức quay trở lại 0.

đang Giản:

Selector selector = Selector.open(); 

SocketChannel channel; 

for (...) { // for each node 
    // Create and connect channels... 
    ... 

    channel.configureBlocking(false); 
    channel.register(selector, SelectionKey.OP_READ, someRelatedObject); 
} 

int ready; 
Set<SelectionKey> readyKeys; 
while (true) { 
    ready = selector.select(); 
    readyKeys = selector.selectedKeys(); 

    System.out.println("Ready channels: " + ready); 
    System.out.println("Selected channels: " + readyKeys.size()); 

    if (ready == 0) { 
    continue; 
    } 

    for (SelectionKey key : readyKeys) { 
    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 

    readyKeys.remove(key); 
    } 
} 

Tại sao select() return 0 mặc dù có một kênh sẵn sàng chưa? Cách đề xuất để giải quyết vấn đề này là gì?

EDIT:

Thay đổi này:

for (SelectionKey key : readyKeys) { 
    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 

    readyKeys.remove(key); 
    } 

này

for (SelectionKey key : readyKeys) { 
    readyKeys.remove(key); 

    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 
    } 

giải quyết vấn đề. Trong một số trường hợp, mã sẽ continue vòng lặp for trước khi nhập remove().

EDIT 2:

Tôi chỉ vừa mới biết rằng foreach vòng lặp của tôi trên các phím chọn thiết là xấu. foreach sử dụng trình lặp của tập hợp. Sửa đổi một bộ sưu tập trực tiếp (không thông qua các phương thức của trình vòng lặp) trong khi lặp lại nó có thể dẫn đến hành vi "tùy ý, không xác định".

Tập hợp khóa đã chọn có thể cung cấp trình lặp số không nhanh. Thiết bị lặp không hoạt động phát hiện các sửa đổi như vậy và ném ConcurrentModificationException vào lần lặp tiếp theo.Vì vậy, sửa đổi các thiết lập trong một foreach hoặc rủi ro hành vi không xác định hoặc có thể gây ra ngoại lệ - tùy thuộc vào việc thực hiện iterator.

Giải pháp: không sử dụng foreach. Sử dụng trình vòng lặp và xóa khóa qua iterator.remove().

Iterator<SelectionKey> iterator; 
SelectionKey key; 
while (true) { 
    // ... 

    iterator = selector.selectedKeys().iterator(); 
    while (iterator.hasNext()) { 
    key = iterator.next(); 
    iterator.remove(); 
    // ... 
    } 
} 

Trả lời

7

select() trả về số lượng các phím đã thay đổi. Vì vậy, nếu khóa đã sẵn sàng trước cuộc gọi select() thì có thể trả về 0 nhưng selectedKeys có thể không trống.

+2

Có thể nếu khóa đã sẵn sàng và không bị xóa? – riha

1

Như bạn đã lưu ý, đó là vì bạn đã không xóa khóa đã chọn khỏi Tập đã chọn.

Như chúng ta thường muốn loại bỏ tất cả phím chọn sau khi xử lý Set, bạn chỉ có thể gọi clear() trên Set rằng sau khi vòng lặp của bạn, có thể là một foreach hoặc bất kỳ loại vòng lặp bạn muốn:

Set<SelectionKey> readyKeys = selector.selectedKeys(); 
for (SelectionKey k : readyKeys) { 
    // Process k 
} 
readyKeys.clear(); 

Bằng cách đó, bạn có thể sử dụng bất kỳ loại vòng lặp nào bạn muốn ("thường xuyên" for hoặc vòng lặp) và đảm bảo tất cả các khóa sẵn sàng sẽ bị xóa, bất kể bạn làm gì bên trong for (bao gồm cả continue có vấn đề). Tùy thuộc vào những gì iterator.remove() thực hiện trong nội bộ, có thể hiệu quả hơn khi gọi clear() một lần thay vì một số iterator.remove() (mặc dù điều đó có thể là tối ưu hóa vi mô).

+0

Không trả lời câu hỏi: "Tại sao' select() 'trả về 0 mặc dù có kênh sẵn sàng?" – EJP

+0

@EJP bạn nói đúng, tôi đã sửa nó. Điều đó có đủ để loại bỏ downvote của bạn? – Matthieu

+0

Ưu điểm của việc xóa bỏ từng 'SelectionKey' thông qua' iterator.remove() 'là gì? – riha

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