.. _ImplicitThreads:

.. raw:: html

   <script>ODSA.SETTINGS.DISP_MOD_COMP = true;ODSA.SETTINGS.MODULE_NAME = "ImplicitThreads";ODSA.SETTINGS.MODULE_LONG_NAME = "Implicit Threading and Language-based Threads";ODSA.SETTINGS.MODULE_CHAPTER = "Concurrency with Multithreading"; ODSA.SETTINGS.BUILD_DATE = "2021-06-01 12:51:47"; ODSA.SETTINGS.BUILD_CMAP = false;JSAV_OPTIONS['lang']='en';JSAV_EXERCISE_OPTIONS['code']='java';</script>


.. |--| unicode:: U+2013   .. en dash
.. |---| unicode:: U+2014  .. em dash, trimming surrounding whitespace
   :trim:


.. This file is part of the OpenCSF eTextbook project. It was
.. auto-generated by scripts from the OpenDSA eTextbook project.
.. See https://OpenCSF.org for more details. OpenCSF is distributed
.. under a Creative Commons Attribution-NonCommercial 4.0 International
.. License (see http://creativecommons.org/licenses/by-nc/4.0/),
.. Copyright (c) 2019-2021 by Michael S. Kirkpatrick. OpenDSA is
.. distributed under an MIT open source license, Copyright (c) 2012-2021
.. by the OpenDSA Project Contributors.

.. avmetadata::
   :author: Michael S. Kirkpatrick
   :requires:
   :satisfies: 
   :topic: 

Implicit Threading and Language-based Threads
=============================================

The POSIX thread library is a powerful and robust mechanism for concurrent
systems programming. However, the library places a significant burden on the
programmer to ensure that the implementation avoids race conditions and other
bugs. In `Synchronization Problems <SynchProblemsOverview.html>`_, we will
examine common patterns that emerge in these types of programs and how to avoid subtle errors.

Since threads were first introduced, language designers have explored a number
of techniques that reduce the complexity and responsibility of managing threads.
This section will examine three different approaches for making multithreading
easier. The first approach is a general style called :term:`implicit threading`
which aims to hide the management of threads as much as possible. The second
approach is to treat threads as objects in languages like Java and Python. The
third (and most modern) approach is to design the language around the concept of
concurrency as a fundamental feature.


Implicit Threading with OpenMP
------------------------------

Implicit threading is the use of libraries or other language support to hide the
management of threads. In the context of C, the most common implicit threading
library is :term:`OpenMP`. OpenMP uses the ``#pragma`` compiler directive to
detect and insert additional library code at compile time. As an example,
consider the prime number calculator from the Extended Examples.
`Code Listing 6.17 <#cl6-17>`_ shows the OpenMP equivalent.

.. _cl6-17:

.. codeinclude:: Threads/CodeListing-6-17.c
   :linenos: true

With implicit threading, the focus of the programmer is on writing the algorithm
rather than the multithreading. The OpenMP library itself takes care of managing
the threads. Specifically, the ``#pragma`` line indicates that OpenMP (``omp``)
should parallelize a for-loop (``parallel for``) with some constraints on the
variables. The OpenMP implementation on that system will then inject code to
perform the thread creation and join.

OpenMP in C is built on top of the pthread library. As such, any code that can
be written using OpenMP can be converted into a more verbose pthread equivalent.
However, the disadvantage of OpenMP is that it only works for certain types of
tasks. There are many types of programs, such as the keyboard listener example,
that can be implemented in pthreads but not OpenMP.

Threads as Objects
------------------

In other languages, traditional object-oriented languages provide explicit
multithreading support with threads as objects. In these types of languages,
classes are written to either extend a thread class or implement a corresponding
interface. This style resembles the pthread approach, as the code is written
with explicit thread management. However, the encapsulation of data within the
classes and additional synchronization features simplify the task.

Java Threads
````````````

Java provides both a ``Thread`` class and a ``Runnable`` interface that can be
used, as shown in `Code Listing 6.18 <#cl6-18>`_. Both require implementing a
public void ``run()`` method that defines the entry point of the thread. Once an
instance of the object is allocated, the thread can be started by invoking the
``start()`` method on it. As with pthreads, starting the thread is asynchronous,
so the timing of the execution is nondeterministic.

.. _cl6-18:

.. codeinclude:: Threads/CodeListing-6-18.java
   :linenos: true

Python Threads
``````````````

`Code Listing 6.19 <#cl6-19>`_ demonstrates two mechanisms for multithreading in
Python. One approach is similar to the pthread style, where a function name is
passed to a library method ``thread.start_new_thread()``. This approach is very
limited and lacks the ability to join or terminate the thread after it starts. A
more flexible technique is to use the threading module to define a class that
extends threading. Similar to the Java approach, the class must have a ``run()``
method that provides the thread's entry point. Once an object is instantiated
from this class, it can be explicitly started and joined later.

.. _cl6-19:

.. codeinclude:: Threads/CodeListing-6-19.py
   :linenos: true

Concurrency as Language Design
------------------------------

Languages such as C, Java, and Python were all designed before :term:`multicore`
architectures rose to prominence in the early 2000s. As such, multithreading
support in these languages was added as a supplement to the language, rather
than a core feature. These languages were originally designed for a
uniprocessing procedural or object-oriented paradigm. As a result, the memory
models that underlie these languages are not adequate to prevent race
conditions. The multithreading libraries had to provide additional features that
allowed programmers to synchronize access to shared data. Or, put another way,
programmers were forced to do extra work to make their programs work correctly.

Newer programming languages have avoided this problem by building assumptions of
concurrent execution directly into the language design itself. For instance, Go
combines a trivial implicit threading technique (goroutines) with channels, a
well-defined form of message-passing communication. Rust adopts an explicit
threading approach similar to pthreads. However, Rust has very strong memory
protections that require no additional work by the programmer.

Goroutines
``````````

The Go language includes a trivial mechanism for implicit threading: place the
keyword go before a function call. In `Code Listing 6.20 <#cl6-20>`_, the line
go ``keyboard_listener(messages)`` launches a new thread that will execute the
keyboard listener function. The new thread is passed a connection to a
message-passing *channel*. Then, the main thread calls
``success := <-messages``, which performs a blocking read on the channel. Once
the user has entered the correct guess of 7, the keyboard listener thread writes
to the channel, allowing the main thread to progress.

Channels and :term:`goroutines <goroutine>` are core parts of the Go language,
which was designed under the assumption that most programs would be
multithreaded. This design choice streamlines the development model, allowing
the language itself to bear the responsibility for managing the threads and scheduling.

.. _cl6-20:

.. codeinclude:: Threads/CodeListing-6-20.go
   :linenos: true

Rust Concurrency
````````````````

Rust is another language that has been created in recent years, with concurrency
as a central design feature. `Code Listing 6.21 <#cl6-21>`_ illustrates the use
of ``thread::spawn()`` to create a new thread, which can later be joined by
invoking ``join()`` on it. The argument to ``thread::spawn()`` beginning at the
``||`` is known as a closure, which can be thought of as an anonymous function.
That is, the child thread here will print the value of ``x``.

.. _cl6-21:

.. codeinclude:: Threads/CodeListing-6-21.ru
   :linenos: true

However, there is a subtle point in this code that is central to Rust's design.
Within the new thread (executing the code in the closure), the ``x`` variable is
distinct from the ``x`` in other parts of this code. Rust enforces a very strict
memory model (known as *ownership*) which prevents multiple threads from
accessing the same memory. In this example, the move keyword indicates that the
spawned thread will receive a separate copy of ``x`` for its own use. Regardless
of the scheduling of the two threads, the main and child threads cannot
interfere with each other's modifications of ``x``, because they are distinct
copies. It is impossible for the two threads to share access to the same memory.

In this small example, the issue of ownership may not seem to be a big deal.
However, if you learn more about Rust and concurrency, you'll quickly realize
that it is. Ownership makes Rust very unique and makes it a very powerful
language for concurrent programming. The crux is that ownership completely
eliminates several types of race conditions, since it is impossible for multiple
threads to share the same memory location. Furthermore, it achieves this memory
safety **without imposing any run-time performance penalty**. Ownership
constraints are checked and enforced at compile-time. This combination of memory
safety and efficient performance gives Rust a significant advantage over other
languages in regard to multithreading.


.. avembed:: Exercises/Threads/ImplicitThreadSumm.html ka
   :module: ImplicitThreads
   :points: 1.0
   :required: True
   :exer_opts: JXOP-debug=true&amp;JOP-lang=en&amp;JXOP-code=java
   :long_name: Implicit threading questions
   :threshold: 4

