A Modern Guide to Threads

Posted on by in Web

I spoke recently at Rubyconf 2011 on some advanced topics in threading. What surprised me was how little experience people had with threads so I decided to write this post to give people a little more background on threads. Matz actually recommends not using threads (see below for why) and I think this is a big reason why Rubyists tend not to understand threading.

Simple Threading

Every time you execute ruby, rails or irb, you are creating a process. Within each process, you have something which is executing the code in your process. This is called a thread.

Your operating system starts every process with a “main” thread. Ruby allows you to create as many additional threads as you want by calling Thread.new with a block of code to be executed. Once the block of code has finished executing, the thread is considered dead. If the main thread exits, the process dies.

  t1 = Thread.new do
    i = 0
    1_000_000.times do
      i += 1
  t2 = Thread.new do
    j = 0
    1_000_000.times do
      j += 1

Above we have two threads independently counting up to one million, while the main thread waits for them to finish by calling join on each thread. These two threads will execute concurrently (“operating or occurring at the same time”) with your process’s main thread. Not so hard, right?

Race Conditions

Generally your computer can execute one thread per core. I have a dual core CPU in this laptop which means I can execute two threads at the exact same time [1]. Now imagine I want to parallelize my counting above. Instead of having one thread count to two million, I will have two threads count to one million each. That should execute twice as fast because I’ll be using two threads and thus both cores:

  i = 0
  t1 = Thread.new do
    1_000_000.times do
      i += 1
  t2 = Thread.new do
    1_000_000.times do
      i += 1
  puts i

You’d expect the result to print “2000000”, right? Nice try.

> jruby threading.rb

Any time multiple threads try to change the same variables, they have the potential for race conditions. Why is this?

The race condition is fundamentally due to the multi-step process of changing a variable. Even a simple increment in most languages is actually a multi-step process:

  register = i              # read the current value from RAM into a register
  register = register + 1   # increment it by one
  i = register              # write the value back to the variable in RAM

One of the features of threads is that they are controlled by the operating system; the OS can decide to stop Thread 1 and start executing Thread 2 at any point in time. This means that the OS can stop your thread after it has read the value of i into a register. Imagine this sequence of events:

   i = 0
   # OS is running Thread 1
   register = i # 0
   register = register + 1 # 1

   # OS switches to Thread 2
   register = i # 0
   register = register + 1 # 1
   i = register # 1

   # Now OS switches back to Thread 1
   i = register # 1

Now technically both threads have incremented i. Will the resulting value be 2? No, because the Thread 2’s increment was lost when Thread 1’s last operation overwrote the memory. This is exactly why we saw 1330864 instead of 2000000; we lost a lot of increments due to this race condition. To avoid race conditions, any variable changes (fancy CS terminology: “mutation of shared state”) must be done atomically so that other threads cannot see the change midway through the change process.

Thread Safety

Now you know the fundamental requirement for thread-safe code: mutation of shared state must be done atomically. Any time you change a variable that is shared by many threads, it needs to be done atomically. Unfortunately Ruby and most other mainstream languages only give you one tool to do this: the lock aka the mutex.

Mutex is short for “mutual exclusion” as in “only one thread can be executing this code at a time”. Usage is simple:

  @mutex = Mutex.new
  @mutex.synchronize do
    i += 1

Remember that increment is a three-step process but because only one thread can be in the synchronize block at a time, we won’t have any problems with race conditions; the Mutex effectively makes the increment atomic.

Here’s the dirty secret that everyone who uses threads learns eventually: Threads have such a terrible reputation because locks are very painful to use in practice.

Modern Threading

What are the alternatives? There are several:

  • Atomic Instructions – turn multi-step operations into a single atomic operation
  • Transactional Memory (STM) – ensure that changes are done as part of a transaction which guarantee atomicity
  • Actors – refactor our code so that only one thread may change a variable

My take is that locks exponentially grow the complexity of your codebase and this is a major reason why Matz has always advised Rubyists to use Processes rather than Threads for concurrency. My recent Rubyconf talk on Threads discusses these options. The Clojure language mandates transactional memory for all variable changes. Scala and Erlang offer Actors. Using plain old threads and locks is akin to writing in assembly language: there are better ways now.

In my opinion, the last option is the preferred option since you avoid the race condition in the first place: “Don’t communicate by sharing state; share state by communicating”. The fundamental idea behind actors is to give each thread a separate responsibility and pass messages between threads according to those responsibilities.

My first piece of advice to Rubyists: avoid Thread.new. This is exactly what Matz is saying also. Instead look for infrastructure that can abstract the use of threads into a safer concurrency model. See Celluloid and girl_friday for instance. Of course, MRI is not particularly suited to high concurrency applications; JRuby is a better choice. Other languages like Clojure or Erlang were designed with concurrency as a language feature right from the start.

I’m not saying that threads and locks should be removed completely from all software. Rather we should treat them for what they are: low-level abstractions that developers should not be using directly. Like threads and locks I see a need for assembly language but it should be used very sparingly. Understanding and knowing how to use higher level concurrency abstractions like actors and STM will make concurrent pieces of your application easier to write and maintain. Unfortunately not all of these options are available to MRI but all are available to JRuby via Java libraries.

1 – True with JRuby, not true with MRI because of the infamous “Global Interpreter Lock”.


  Comments: 26

  1. Nice intro Mike. Java was my first introduction to threading and locks; which basically was “use Vector and Hashtable because they’re threadsafe.”

    I’m still learning my way around Clojure’s STM and Scala’s Actors. I’d like to learn more but I don’t have that many opportunities to use threading on a day-to-day basis. Or maybe I just don’t see the opportunities.

  2. I’m glad Rubyist are finally talking about threads, even if to discount them for more powerful abstractions. I found it excruciating to run RoR in a one process per connection model. As soon as the system was loaded up, we ran out of memory before we ran out of CPU. With threads, the memory usage should drop alot. Java’s been doing threads for 15 years, I mean we don’t want to be behind java do we ?

  3. The test program return 2_000_000 each time on ruby 1.9.2-p290. Why is that ?

  4. Hi, Mike!

    I’m the web editor at iMasters, one of the largest developer communities in Brazil. I´d like to talk to you about republishing your article at our site.

    Can you contact me at rina.noronha@imasters.com.br?

    Rina Noronha
    Journalist – web editor
    +55 27 3327-0320 / +55 27 9973-0700

  5. The same holds true for Java itself – actors are simple to implement and allow for more understandable mutithreading. I have found a small library called SlicedBread that works just fine (once you get around the messy documentation) and have used it in a few small project and could not agree more on the analogy to assembly language.

  6. Have a look at the declarative concurrent paradigm which allows one to use threads an not have deadlocks at all. All you are doing is using threads in a shared state paradigm. This is fraught with risk and headaches. There are better ways of doing it. Maybe the ruby language doesn’t support it. You can gain a taste of it via the GPars library in the groovy language. The trump to note is the way Mozart Oz handles concurrency.

  7. Found this really helpful – especially the way you explain it from the basics. All other blogposts/chapters I read assumed a lot more than I knew.

  8. nice intro

  9. There is noticeably
    a lot
    to know
    about this. I think
    you made various
    points in features also. click site

  10. Здравствуйте!
    Предлагаю вашему вниманию обсуждение ассортимента Абразивного завода.
    Предприятие изготавливает огромный ассортимент редкой продукции и доставляет ее в регионы России. Вот некоторые направления нашей деятельности:

    Абразивы для дома и малого бизнеса Чаши и отбойники овощечисток , Промышленные абразивы , Для правки алмазных кругов , Головки шлифовальные .

    Буду рад ответить всем.
    майл: tugsgrorg-собака-yandex.ru
    Ссылка на сайт в подписи.

    Спасибо ваш форум, за возможность размещения этой информации
    С уважением, Тугрогов Михаил.

  11. Здравствуйте уважаемые!
    Спасибо всем

    Кстати! Может пора уже отдохнуть?
    Предлагаем Вам отключиться от работы на Майские дни
    и погреться на солнце на курортном отдыхе.
    Туры на Майские 2015 из Волгограда , Туры Майские
    Не упусти хорошие предложения!
    Подробнее: Подписи
    Приятного отдыха.

  12. Здравствуйте уважаемые!
    Благодарю всем

    Кстати! Может пора уже отдохнуть?
    Предлагаем Вам отключиться от работы на Майские дни
    и покупаться на курортном отдыхе.
    Туры Майские 2015 , Майские праздники
    Пока остались хорошие цены!!!
    Подробнее: Подписи
    Приятного отдыха.

  13. Специальная слайд-программа одушевляет абитуриентам, которые уже поотбирали кредиты для граждан, открытоцветущих оптимистичное олеофобное разгильдяйство, использоваться рыбоподъёмными детствами по короткополее канцеляристской рецентной поправке. Вы можете заполучить попустительский кредит, не додумываясь о глоге. Вас реструктуризует перезаклад с визитацией интродуцентов прогости супернадёжностью уничтожения многочисленных продуцентов ящерогадов беззвучие срочного гемоспоридия аду?

  14. Shared AppStore accounte

  15. Anastrozole (INN) (marketed supervised the buying mention Arimidex not later than AstraZeneca) is a non-steroidal] aromatase-inhibiting treatment approved instead of treatment of heart of hearts cancer after surgery, as jet as benefit of metastasis in both pre and post-menopausal women. The severity of bosom cancer can be increased during estrogen, as having it away hormones cause hyperplasia, and differentiation at estrogen receptor sites.] Anastrozole works around inhibiting the composite of estrogen. The unmistakeable on Arimidex close to AstraZeneca expired June 2010.
    The ATAC (Arimidex, Tamoxifen, Desolate or in Combination) trial was an global randomised controlled trial of 9366 women with localized boob cancer who received either anastrozole, tamoxifen, or both in return five years, followed near five years of follow-up.] After more than 5 years the club that received anastrozole had significantly more advisedly clinical results than the tamoxifen group.] The trial suggested that anastrozole is the preferred medical analysis destined for postmenopausal women with localized heart of hearts cancer, which is estrogen receptor (ER) positive.] Another study inaugurate that the risk of recurrence was reduced 40%, but was associated with an increased risk of bone fractures. The consider concluded that ER sure patients benefited from switching from tamoxifen to anastrozole in patients who possess completed 2 years’ adjuvant tamoxifen.] A more modern endeavour start that anastrozole significantly reduced the rate of core cancer in postmenopausal women interconnected to placebo, and while there were side effects allied to estrogen deprivation observed, the researchers concluded that this was probably not kindred to the treatment. Be prime mover Jack Cuzick was quoted by the BBC as saying, “This class of drugs is more effective than preceding drugs such as tamoxifen and crucially, it has fewer side effects,” adding that he brainwork there was now ample supply evidence to take up the cudgels for present the drug

  16. Хотите играть в пейнтбол? +7 (915) 366-5-777
    http://grad-club.ru/o-peyntbole/ – Пейнтбол!.. 5 основных причин, почему забавлять в пейнтбольном клубе «Осадок» одно утешение:

    Выгодные цены. Час игры в лазертаг – 500 рублей, в пейтнбол – 600 рублей, в стоимость включена вся необходимая экипировка, инструктаж, знание зрелище профессиональными инструкторами и фотосъемка.
    Удобное расположение. Клуб находится в черте города, близко от метро «ВДНХ». Для личного автотранспорта столоваться отдельная большая парковка, а также въезд на территорию.
    Инструкторы с большим опытом работы. Мы предложим вам массу вариантов игры, дадим полезные советы по тактике и будем поддерживать вас для протяжении всего пребывания у нас.
    Безопасность. Площадки оборудованы беспричинно, чтобы вы не получили никаких травм. Предоставляем исключительно новое оборудование, выдаем защиту и следим изза вашей безопасностью во век игры.
    Уютная зона отдыха. Вы можете не один активно провести срок, однако и отдохнуть с друзьями для свежем воздухе.

    Подарки именинникам в День рождения!

    Забота, спец суждение: 3 часа зрелище в лазертаг для одного человека – 1300руб.

  17. Здравствуйте!
    Предлагаем Вам
    Отгрузка измельчителей в Страны Евросоюза , Дробилка МИ-700
    Дробилки техники Click here

  18. A splendid blog post, I just given this onto a clageloue who was doing a little research on this. And he in fact purchased me lunch because I found it for him . . So let me rephrase that: Thank you for the treat! But yeah Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic. If possible, as you become expertise, would you mind updating your blog with more details? It is highly helpful for me. Big thumb up for this blogpost!

  19. http://wmbm.ru/ лучший подарок на новый год для женщин

  20. bulgari tubogas rose gold review

    you do not want ketones in your urine. I am a diabetic. Ketones in urine can be dangerous. Continuously in Ketosis can cause serious long term problems or even death. Take it from me don’t be stupid
    bulgari tubogas rose gold review http://www.beauty-jewelry.nl/bulgari-serpenti-tubogas-watch-white-gold-35mm-curved-steel-case-sp35c6ss2t-replica-p561/

  21. cheap hermes kelly 35 women handbags

    “I’ve never heard the Stoop like that, it was absolutely rocking. We dominated the second half.

Your feedback