2010-03-03 27 views
5

Tôi cần tải lên một loạt tệp trong một thư mục tới S3. Vì hơn 90% thời gian cần thiết để tải lên được chi tiêu chờ đợi yêu cầu http hoàn thành, tôi muốn thực hiện một vài trong số chúng cùng một lúc bằng cách nào đó.Cách sử dụng sợi ruby ​​để tránh chặn IO

Sợi có thể giúp tôi điều này không? Chúng được mô tả như một cách để giải quyết loại vấn đề này, nhưng tôi không thể nghĩ ra bất kỳ cách nào tôi có thể thực hiện bất kỳ công việc nào trong khi một cuộc gọi http chặn.

Bất kỳ cách nào tôi có thể giải quyết vấn đề này mà không có chủ đề?

+0

Vì vậy, bất kỳ ai cũng có thể tự nhận xét về sợi? Tôi có đúng trong giả định rằng các sợi không có quyền hạn "làm công cụ trong nền" không? –

Trả lời

3

Tôi không lên trên sợi ở 1.9, nhưng Chủ đề thường xuyên từ 1.8.6 có thể giải quyết vấn đề này. Hãy thử sử dụng Hàng đợi http://ruby-doc.org/stdlib/libdoc/thread/rdoc/classes/Queue.html

Nhìn vào ví dụ trong tài liệu, người tiêu dùng của bạn là phần tải lên. Nó 'tiêu thụ' một URL và một tập tin, và tải lên dữ liệu. Nhà sản xuất là một phần của chương trình tiếp tục làm việc và tìm các tệp mới để tải lên.

Nếu bạn muốn upload nhiều file cùng một lúc, bạn chỉ cần khởi động một chủ đề mới cho mỗi file:

t = Thread.new do 
    upload_file(param1, param2) 
end 
@all_threads << t 

Sau đó, sau này trong 'sản xuất' mã của bạn (trong đó, hãy nhớ rằng, không nhất thiết phải trong Chủ đề của chính nó, nó có thể là chương trình chính):

@all_threads.each do |t| 
    t.join if t.alive? 
end 

Hàng đợi có thể là @member_variable hoặc $ toàn cầu.

+1

Mặc dù sợi questino của tôi dường như đã không được trả lời –

1

Bạn có thể sử dụng quy trình riêng biệt cho điều này thay vì chủ đề:

#!/usr/bin/env ruby 

$stderr.sync = true 

# Number of children to use for uploading 
MAX_CHILDREN = 5 

# Hash of PIDs for children that are working along with which file 
# they're working on. 
@child_pids = {} 

# Keep track of uploads that failed 
@failed_files = [] 

# Get the list of files to upload as arguments to the program 
@files = ARGV 


### Wait for a child to finish, adding the file to the list of those 
### that failed if the child indicates there was a problem. 
def wait_for_child 
    $stderr.puts " waiting for a child to finish..." 
    pid, status = Process.waitpid2(0) 
    file = @child_pids.delete(pid) 
    @failed_files << file unless status.success? 
end 


### Here's where you'd put the particulars of what gets uploaded and 
### how. I'm just sleeping for the file size in bytes * milliseconds 
### to simulate the upload, then returning either +true+ or +false+ 
### based on a random factor. 
def upload(file) 
    bytes = File.size(file) 
    sleep(bytes * 0.00001) 
    return rand(100) > 5 
end 


### Start a child uploading the specified +file+. 
def start_child(file) 
    if pid = Process.fork 
     $stderr.puts "%s: uploaded started by child %d" % [ file, pid ] 
     @child_pids[ pid ] = file 
    else 
     if upload(file) 
      $stderr.puts "%s: done." % [ file ] 
      exit 0 # success 
     else 
      $stderr.puts "%s: failed." % [ file ] 
      exit 255 
     end 
    end 
end 


until @files.empty? 

    # If there are already the maximum number of children running, wait 
    # for one to finish 
    wait_for_child() if @child_pids.length >= MAX_CHILDREN 

    # Start a new child working on the next file 
    start_child(@files.shift) 

end 


# Now we're just waiting on the final few uploads to finish 
wait_for_child() until @child_pids.empty? 

if @failed_files.empty? 
    exit 0 
else 
    $stderr.puts "Some files failed to upload:", 
     @failed_files.collect {|file| " #{file}" } 
    exit 255 
end 
2

Để trả lời câu hỏi thực tế của bạn:

Sợi có thể giúp tôi với điều này ở tất cả?

Không, chúng không thể. Jörg W Mittag explains why best.

Không, bạn không thể đồng thời với sợi. Các sợi đơn giản không phải là một cấu trúc đồng thời, chúng là một cấu trúc dòng điều khiển, giống như các ngoại lệ. Đó là toàn bộ điểm của sợi: chúng không bao giờ chạy song song, chúng hợp tác và chúng là xác định. Sợi là coroutines. (Trong thực tế, tôi không bao giờ hiểu tại sao chúng không đơn giản được gọi là Coroutines.)

Cấu trúc đồng thời duy nhất trong Ruby là Thread.

Khi anh ấy nói rằng sự đồng thời duy nhất trong Ruby là Thread, hãy nhớ rằng có rất nhiều cách khác nhau của Ruby và chúng thay đổi trong quá trình triển khai luồng của chúng. Jörg một lần nữa provides a great answer cho những khác biệt này; và kết luận chính xác rằng chỉ có một cái gì đó giống như JRuby (sử dụng các chủ đề JVM ánh xạ tới các chuỗi gốc) hoặc giả mạo quá trình của bạn là cách bạn có thể đạt được sự song song thực sự.

Bất kỳ cách nào tôi có thể giải quyết vấn đề này mà không có chủ đề?

Khác với forking quá trình của bạn, tôi cũng sẽ đề nghị bạn nên nhìn vào EventMachine và một cái gì đó giống như em-http-request. Đây là một máy khách HTTP không bị chặn, không được ngăn chặn, được ngăn chặn bởi sự kiện, không đồng bộ và không phải chịu phí trên luồng.

+0

Xem phần Đồng bộ hóa với nhiều giao diện tại https://github.com/igrigorik/em-http-request/wiki/Parallel-Requests – Sairam

1

Aaron Patterson (@tenderlove) sử dụng một ví dụ gần như chính xác như của bạn để mô tả chính xác lý do tại sao bạn có thể nên đề sử dụng để đạt được đồng thời trong tình huống của bạn.

Hầu hết các thư viện I/O hiện đủ thông minh để giải phóng GVL (Khóa máy ảo toàn cầu hoặc hầu hết mọi người biết nó là GIL hoặc Khóa thông dịch toàn cục) khi thực hiện IO. Có một cuộc gọi hàm đơn giản trong C để thực hiện việc này. Bạn không cần phải lo lắng về mã C, nhưng đối với bạn điều này có nghĩa rằng hầu hết các thư viện IO trị giá muối của họ sẽ giải phóng GVL và cho phép các chủ đề khác thực thi trong khi luồng đang làm IO đợi dữ liệu trả về .

Nếu những gì tôi vừa nói là khó hiểu, bạn không cần phải lo lắng quá nhiều. Điều chính bạn cần biết là nếu bạn đang sử dụng một thư viện phong nha để thực hiện các yêu cầu HTTP của bạn (hoặc bất kỳ hoạt động I/O nào khác cho vấn đề đó ... cơ sở dữ liệu, giao tiếp interprocess, bất cứ điều gì), trình thông dịch Ruby (MRI) đủ thông minh để có thể giải phóng khóa trên trình thông dịch và cho phép các luồng khác thực thi trong khi một luồng đang chờ IO trả về. Nếu chuỗi tiếp theo có IO riêng của nó để lấy, trình thông dịch Ruby sẽ làm điều tương tự (giả sử rằng thư viện IO được xây dựng để sử dụng tính năng này của Ruby, mà tôi tin rằng hầu hết là những ngày này).

Vì vậy, để tóm tắt những gì tôi đang nói, hãy sử dụng chuỗi! Bạn sẽ thấy lợi ích hiệu suất. Nếu không, hãy kiểm tra xem liệu thư viện http của bạn có đang sử dụng hàm rb_thread_blocking_region() trong C và, nếu không, hãy tìm hiểu tại sao không. Có thể có lý do chính đáng, có thể bạn cần xem xét sử dụng thư viện tốt hơn.

Các liên kết đến video Aaron Patterson là ở đây: http://www.youtube.com/watch?v=kufXhNkm5WU

Đó là giá trị một chiếc đồng hồ, ngay cả khi chỉ cho cười, như Aaron Patterson là một trong những người hài hước trên internet.

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