Multithreading is the ability to execute code on multiple concurrent threads. Each thread exists within a process, and each process can have at least one thread. Multithreading allows you to speed up your program, but also creates additional problems that do not occur in single-threaded programs. If you decide to use multiple threads in your app, you need to ensure that your code is thread-safe.
Let’s start with this simple program.
And a test for it, to make sure everything works properly.
Time to run our test.
Success! Our program passed test passed. Now, let’s see what the result will be with two threads. To create a new thread, we will use the Thread class, which is an abstraction for an operating system thread. When you create a Thread, you need to pass the code block that is executed inside the thread. To ensure that the main thread will wait for our threads to finish, we have to call join method on each thread.
Let’s run our test.
Alright, it looks like everything works well. But let’s run the same example with JRuby.
Oops, that’s not what we expected! What’s going on? What you see above happened because, unlike with MRI (Matz's Ruby Interpreter), there is no GIL (Global Interpreter Lock) mechanism in JRuby. The GIL ensures that two threads cannot be executed at the same time. That means threads on MRI won't run in parallel - MRI just switches between threads, giving each some CPU time. To fix our code to work with JRuby, we can use the Mutex locking system, which will allow to synchronize access to certain sections of code.
Let’s check if it works.
Ok, fixed. Does that mean you don’t have to care about thread safety when you have GIL? Well, not really. Imagine a situation in which you read some global resource and update its value after a while. We can simulate this with the following example:
Now, let’s run that with MRI.
As we can see, GIL does not ensure that our code is always thread safe. We need to make sure that all operations are atomic. We do that by using the same mechanism as before - Mutex.
If you use threads, you need to make sure that your code and any gem you use is thread safe. Special care should be taken if you use a background processing system, like Sidekiq. By default, one Sidekiq process creates 10 threads. You can change that by changing the concurrency value in the Sidekiq config file.