Working with Thread in Ruby

Photo by Noor Sethi on Unsplash

Thread is the Ruby implementation for a concurrent programming model. Threads are useful for operations: sending or processing HTTP-requests; manipulations with database; I/O operations.

Typical thread example: write code for sending several URL requests. You have an array of URLs, and you should send a request for every URL and calculate response size. Without threads, the code will be like this.

If you have more than 200–300 URLs, the code will be performed too long. Let’s rewrite code with threads for reducing performing time. The code with threads will be faster because it will be executed in parallel.

Another typical example is writing TCP server. You can quickly implement it with TCPServer class and infinity loop, but every request will be processed in a sequence.

If you want to process requests in parallel, you should use threads. This is simple implementation with multi-threading. You just replace ‘socket=server.accept’ by ‘Thread.start(server.accept) do |socket|’.

Thread-pool

How to manage multi-threading? For example, you want to run only 2, 5 or 10 threads. If you’re going to do it, use Thread-pool pattern. It can help you set thread-limit.

Thread-pool. Image from Wikipedia.

Here is the implementation of Thread-pool. Thread-pool has a queue of tasks and threads array. Each thread takes one task from queue, does jobs and takes another task from the queue.

When will Thread-pool be useful? It is helpful for processing bunch operations from an array or a queue. For example, you calculate math operations with I/O: calculating SHA256 of various string to find the result with X amount of leading 0’s. You were given a string ‘hello world!<number>’. You should replace <number> with a number to result SHA256 must be with 1 leading 0's.

More visually:

SHA256 of ‘hello world!1’ is 8053f81c65ba639c6aa54118f111d9cbe275bf410bb6a4b2ec70a03f9629ffc0
SHA256 of ‘hello world!2’ is 79d11adb260a7fec1e6ac6deb88445a83eaed84a7e05f1a82adcaa8ee4610d27
SHA256 of ‘hello world!3’ is 7b0c15fca0538de4202a7db1c9c1690ca377099fc93d6915a2e2ad11a4c55e0c
….
SHA256 of ‘hello world!9’ is 0fe33fac0dc830c7d9004cb1c9e2d2940fabcd21828a6f68a38653a3dd3b8b4c

WOW! SHA256 of ‘hello world!9’ is the string with 1 leading 0’s. Let’s write code for finding string with 3 leading 0's.

For masOS, I am using sha2 CLI-tool. For Linux, you can use sha256sum. Each executing CLI-tool is I/O operation and we can reduce performing time by using threads.

And result is:

string: 'hello world!'
number: '3727'
hash: '000112a685cacf5d0a99fde15a9116796244d4550cb945f0193404c428048414'
time: '1'(minutes), '37'(seconds)

Let’s solve the task by Thread-pool. It will be performing faster.

And result is:

string: 'hello world!'
number: '3727'
hash: '000112a685cacf5d0a99fde15a9116796244d4550cb945f0193404c428048414'
time: '0'(minutes), '19'(seconds)

Limitation

What about thread limit? Each computer hash threads limit. You can see it by runing the script.

10_000.times do |i|
Thread.new { sleep }
rescue ThreadError
puts "Your thread limit is #{i} threads"
Kernel.exit(true)
end

Resume

Thread is a tool for running code in parallel. If you see a queue or an array of I/O operations — use threads for better performing.