An Introduction to Multithreading

A program is ‘multithreading’ or ‘running concurrently’ when it is doing multiple things at once. In a basic program, you will step through one operation at a time. We all have learned basic looping over an array. But what if our program could run two instances at once, each over half the array? If each instance was as fast as the original, they could cover the array together, and in half the time.

A basic looping function in Ruby:

def get_sum_of_two_arrays(a1, a2)    sum_a1 = 0
a1.each {|e1| sum_a1 += e1}
sum_a2 = 0
a2.each {|e2| sum_a2 += e2}
return sum_a1 + sum_a2
end

But how would this happen with two concurrent threads? Let’s spitball something:

def get_sum_of_two_arrays_with_threads(a1, a2)    # Does not need a return value immediately
Thread(get_sum_of_one_array(a1))
Thread(get_sum_of_one_array(a2)
# Now we need some way to combine the result of the two
return result of threads
end

But hold on! That was not necessary, nor was it a good example. Why would we bother doing that for such a small thing? In fact, few simple programmatical examples need multithreading. Multithreading is most interesting, and most useful, in complex scenarios. Many introductions to threads use concurrency in unnecessary situations. I dislike this approach. Let’s try to build something that is simple, but still shows the usefulness of multithreading.

Consider an online multiplayer game. In a game, there are several things happening at once:

  • The game is being rendered
  • You are sending data to the server (your actions in-game)
  • You are receiving from the server (other people’s actions in-game)
Image for post
Image for post
Even if you lose your connection, you should still be able to fish

In this example, we *need* to handle multiple things at once. We can’t just run a linear block:

while true    render_game()
receive_data() # have to wait for this?
send_data()
end

Networks are unreliable, and your data from the server will not come in nice, clean increments. The server may send the first packet after 5ms, but then you may lose connection for 1000ms. The player avatar should not freeze in place while you are waiting for data. The game should continue to run in the meantime, then correct things after the lag. We could consider something like this:

while true    render_game()
if receive_data() # just wait & try to receive some data
=> then do something with the received data
end
send_data()
end

But we will still have to wait on the receive_datafunction. It will be difficult to predict how long the transmission takes. How long should we wait?

If we listen for server data on another thread, we can run our tasks independently. The only question is how to get them to communicate. Let’s try some pseudocode:

$game_data = {SOME_INITIAL_VALUE}tell some thread(wait_for_incoming_data)
then, using(incoming_data)
$game_data = incoming_data
end
end
while true
render_game($game_data)
send_data(whatever_the_player_is_doing)
end

Note how our two threads communicate through $game_data. Now if the server or network has some hiccups, so be it! The game will continue to run, and just experience some lag (hopefully for no longer than a few ms). Player experience will be less interrupted.

At the end of this, I will go through an example of multithreading in Ruby. However, I believe that the important aspect is to understand the larger picture. With that in mind, let’s consider some potential problems that can arise.

Common problems

Problems with concurrency can be complex and difficult to debug. Due to multiple sources of modification, you can experience unintuitive issues.

  • Security Issues: if a variable is accessible at a high level, this in turn makes it more vulnerable
  • Visibility Issues: a thread reads shared data before it is changed, but is unaware of an update to the data
  • Access Issue/Race Conditions: occur when multiple threads attempt to change a shared value at one time. This is one of the biggest concerns with multithreading, and its fixes provide their own issues, such as deadlocks and inefficient locking methods.

Ruby example

This example prints a pattern, but accommodates outside changes. It prints linearly increasing values, but changes the delta depending on user input.

$delta = 5def pattern
i = 0
while true
puts i
i = i + $delta
sleep(3) # pauses program for 3 seconds
end
end
def change_delta
while true
i = gets.chomp.to_i
$delta = i
puts “Changed delta to: #{i}”
end
end
t1 = Thread.new{pattern()}
t2 = Thread.new{change_delta()}
t1.join()
t2.join()

Note that our example is still a bit dangerous. We are using a global variable to communicate.

TutorialsPoint has an even more simple example. I use ours to illustrate how different threads can communicate.

OWASP

vogella

good ol’ StackOverflow

Written by

NYC, fullstack

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store