-- Back to Index --
To understand the thread, one needs to fully grasp the lifecycle of thread i.e. from start to finish.
In Perl, the only threading model available is ithreads (interpreter threads), which is implemented via the threads module. In Perl, threads are ithreads, meaning each thread has its own Perl interpreter. Data is not shared by default, and you must explicitly use threads::shared to share variables. A lock is used to ensure thread-safe access to shared data.
+-----------------+---------------------------+--------------------------------+ | Feature | Python | Perl | +-----------------+---------------------------+--------------------------------+ | Memory Sharing | Shared by default | Not shared by default | | Thread Model | Lightweight (OS-managed) | Heavyweight (Perl interpreter) | | Thread Safety | Requires explicit locks | Requires explicit locks | | Data Sharing | Automatic (shared memory) | Explicit (via threads::shared) | | Performance | Faster (less overhead) | Slower (more overhead) | +-----------------+---------------------------+--------------------------------+ In this post, I would share how we deal with it in Perl and Python.
In Perl, we would do something like below as the main process creates a new thread.
#!/usr/bin/env perl use v5.38; use threads; sub worker($id) { say "Thread $id started."; sleep 5; say "Thread $id finished."; } say "Main process started."; threads->create(\&worker, 1); say "Main process finished."; NOTE: Here threads->create() actually creates a new thread and immediately start executing it.
Let's see what happens when we run the code:
$ perl create-thread.pl Main process started. Main process finished. Perl exited with active threads: 1 running and unjoined 0 finished and unjoined 0 running and detached Thread 1 started. $ You see the output, it tells you the whole story.
Did you notice, the main process didn't wait for the thread to finish?
And in Python, we would do something like this:
#!/usr/bin/env python3 import threading import time def worker(id): print(f"Thread {id} started.") time.sleep(5) print(f"Thread {id} finished.") print("Main process started.") thread = threading.Thread(target=worker, args=(1,)) thread.start() print("Main process finished.") Now running the above code, we get this:
$ py create-thread.py Main process started. Thread 1 started. Main process finished. Thread 1 finished. $ Imagine, if something bad happens in the worker routine, the main process wouldn't know about it.
It just created a new thread and walked away.
We can get around the above issue by joining the newly created thread to the calling process.
This is how you would do in Perl:
#!/usr/bin/env perl use v5.38; use threads; sub worker($id) { say "Thread $id started."; sleep 5; say "Thread $id finished."; } say "Main process started."; threads->create(\&worker, 1)->join; say "Main process finished."; Now if we run the updated code, we get this:
$ perl join-thread.pl Main process started. Thread 1 started. Thread 1 finished. Main process finished. $ This is much better, right?
Let's do the same in Python now:
#!/usr/bin/env python3 import threading import time def worker(id): print(f"Thread {id} started.") time.sleep(5) print(f"Thread {id} finished.") print("Main process started.") thread = threading.Thread(target=worker, args=(1,)) thread.start() thread.join() print("Main process finished.") Now running the above code, we get this:
$ py join-thread.py Main process started. Thread 1 started. Thread 1 finished. Main process finished. $ NOTE: In Perl, calling $thread->join returns the result back to the calling process.
Let's update the above example:.
#!/usr/bin/env perl use v5.38; use threads; sub worker($id) { say "Thread $id started."; sleep 5; say "Thread $id finished."; return $id * 2; } say "Main process started."; my $thread = threads->create(\&worker, 1); say "Result from thread: ", $thread->join; say "Main process finished."; Run the code to see the result.
$ perl result-thread.pl Main process started. Thread 1 started. Thread 1 finished. Result from thread: 2 Main process finished. $ OK, can we do the same in Python?
Yes, you can but not that straight forward. One way is to use queue.Queue as below:
#!/usr/bin/env python3 import time import queue import threading def worker(id, result): print(f"Thread {id} started.") time.sleep(5) print(f"Thread {id} finished.") result.put(id * 2) print("Main process started.") result = queue.Queue() thread = threading.Thread(target=worker, args=(1, result)) thread.start() thread.join() while not result.empty(): print("Result from thread: ", result.get()) print("Main process finished.") Let's check the result now:
$ py result-thread.py Main process started. Thread 1 started. Thread 1 finished. Result from thread: 2 Main process finished. $ If you really want to detach the newly created thread from the main process then you can do it explicitly.
In Perl, we would do something like this:
#!/usr/bin/env perl use v5.38; use threads; sub worker($id) { say "Thread $id started."; sleep 5; say "Thread $id finished."; } say "Main process started."; threads->create(\&worker, 1)->detach; say "Main process finished."; Run the above code and you should see this:
$ perl detach-thread.pl Main process started. Main process finished. $ In Python, you create thread as daemon and it would go away as shown below:
#!/usr/bin/env python3 import threading import time def worker(id): print(f"Thread {id} started.") time.sleep(5) print(f"Thread {id} finished.") print("Main process started.") thread = threading.Thread(target=worker, args=(1,), daemon=True) thread.start() print("Main process finished.") Now running the above code, we get this:
$ py detach-thread.py Main process started. Thread 1 started. Main process finished. $ Now the big question, when would you do this?
These are situations when:
1. You want a thread to run independently 2. You want to avoid blocking the main thread 3. You want the thread to clean up automatically 4. You don't want to wait for the thread to finish -- Back to Index --